jibeinc/juice

View on GitHub
src/LocationTypeahead/index.js

Summary

Maintainability
A
1 hr
Test Coverage
require('./styles.css');
const currentLocationTemplate = require('./useMyCurrentLocation.tmpl');
const Typeahead = require('../Typeahead');
const LocationTextInput = require('../LocationTextInput');
const FragFactory = require('../BaseFragmentFactory');
const CurrentLocation = require('../CurrentLocation');
const Utils = require('../Utils.js');

/**
 * Location Search Typeahead
 * Extends `Typeahead` class to:
 * - add a "use my current location" icon to text input
 * - add fixed result that triggers "use my current location" on click
 */
class LocationTypeahead extends Typeahead {
  /**
   * Create a new LocationTypeahead
   * @param {string} el - The selector for the element to put the LocationTypeahed in
   * @param {function} fetch - The function to call to fetch/refresh results
   * @param {object} opts - The options for the component
   * @param {string} [opts.currentLocationText] - A string to display for "use my location"
   * @param {*[]} [opts.fixedResults] - An array of results to always display
   * @param {object} [opts.textInputOpts] - An object to pass opts to the textInput component
   */
  constructor(el, fetch, opts = {}) {
    const currentLocationText = opts.currentLocationText || 'Use my current location';
    const showLoaderOnCurrentLocation = opts.showLoaderOnCurrentLocation || false;

    // define the "current location" icon DOM fragment
    const iconFactory = new FragFactory({
      render: (data) => {
        data.displayValue = data.name === 'listItem' ? 'inline' : 'none';
        data.currentLocationText = currentLocationText;
        return currentLocationTemplate(data);
      },

      controller: (data) => {
        const currentLocationIcon = new CurrentLocation('.ui-current-location-' + data.name, {
          showLoaderOnCurrentLocation
        });

        currentLocationIcon.subscribe((event) => {
          if (event.isLocation) {
            event.listItem = true; // set this to prevent repeating
            this.set(event);
          }
        });
        currentLocationIcon.render();
      }
    });

    // setup "current location" fixed result
    opts.fixedResults = (opts.fixedResults || []).concat([{
      useMyCurrentLocation: true,
      preSelectHook: () => {
        $('.ui-current-location-listItem').trigger('click'); // trigger 'use my location' icon
        return false; // don't run normal selection behavior
      }
    }]);

    // Add the same "use my current location" option to the text input
    opts.textInputOpts = opts.textInputOpts || {};
    Object.assign(opts.textInputOpts, {
      currentLocationText
    });

    super(el, fetch, opts);

    this.iconFactory = iconFactory;
    this.$el.addClass('ui-location-typeahead');
  }

  /**
   * Handles when the text input updates and sets the Typeahead
   */
  handleTextInputUpdates() {
    this.textInput.subscribe((v) => {
      if (Utils.isPlainObject(v) && v.isLocation && !v.listItem) {
        this.setInternal(v);
      }
    });

    super.handleTextInputUpdates();
  }

  /**
   * Render the item based on the supplied iconFactory
   * @param {object} item - The item to display in the ListView
   * @returns {*} The markup for the icon
   */
  renderItem(item) {
    if (item && item.useMyCurrentLocation) {
      return this.iconFactory.make({
        name: 'listItem'
      });
    } else {
      return super.renderItem(item);
    }
  }

  /**
   * Sets the value of the LocationTypeahead
   * @param {string} v - The value for the LocationTypeahead
   * @returns {LocationTypeahead} The instance
   */
  set(v) {
    if (v && !Utils.isPlainObject(v)) {
      v = Utils.sanitizeText(v);
    }

    this.textInput.set(v);
    this.setInternal(v);
    this.textInput.$el.find('input').trigger('blur');
    return this;
  }

  /**
   * Creates a new LocationTextInput for use with the LocationTypeahead
   * @param {object} textInputOpts - The options for the LocationTextInput
   * @returns {LocationTextInput} A new LocationTextInput
   */
  setupTextInput(textInputOpts) {
    return new LocationTextInput(this.$el.find('.input-container'), textInputOpts);
  }
}

module.exports = LocationTypeahead;