Source: ol/FeatureTip.js

/**
 * @module nyc/ol/FeatureTip
 */

import $ from 'jquery'

import OlOverlay from 'ol/Overlay'

import nyc from 'nyc'

/**
 * @desc A class for providing tool tips on mouseover for vector features
 * @public
 * @class
 * @extends ol.Overlay
 * @see http://openlayers.org/en/latest/apidoc/module-ol_Overlay.html
 */
class FeatureTip extends OlOverlay {
  /**
   * @desc Create an instance of FeatureTip
   * @public
   * @constructor
   * @param {module:nyc/ol/FeatureTip~FeatureTip.Options} options Constructor options
   */
  constructor(options) {
    const element = $(options.map.getTargetElement()).find('.f-tip').get(0)
    super({
      id: nyc.nextId('FeatureTip'),
      element: element || $(FeatureTip.HTML).get(0),
      stopEvent: false
    })
    this.setMap(options.map)
    this.map = this.getMap()
    this.tip = $(this.getElement())
    this.addTips(options.tips)
    this.map.on('pointermove', $.proxy(this.label, this))
    $(document).mouseover($.proxy(this.out, this))
  }
  /**
   * @desc Hide the feature tip
   * @public
   * @method
   */
  hide() {
    this.tip.fadeOut()
  }
  /**
   * @desc Adds tip definitions
   * @public
   * @method
   * @param {Array<module:nyc/ol/FeatureTip~FeatureTip.TipDef>} tips The tip definitions to add
   */
  addTips(tips) {
    tips.forEach(def => {
      def.layer.nycTip = def.label
    })
  }
  /**
   * @private
   * @method
   * @param {Object} event Event object
   */
  out(event) {
    if (!$.contains(this.map.getTargetElement(), event.target)) {
      this.hide()
    }
  }
  /**
   * @private
   * @method
   * @param {ol.MapBrowserEvent} event OpenLayers map browser event
   */
  label(event) {
    const label = this.map.forEachFeatureAtPixel(event.pixel, (feature, layer) => {
      return layer.getVisible() && layer.nycTip ? layer.nycTip(feature) : null
    })
    if (label) {
      this.tip.html(label.html)
      this.tip.get(0).className = `f-tip ${label.css || ''}`
      this.setPosition(event.coordinate)
      this.tip.show()
      this.position()
    } else {
      this.hide()
    }
  }
  /**
   * @private
   * @method
   */
  position() {
    const size = this.map.getSize()
    const position = this.map.getPixelFromCoordinate(this.getPosition())
    const width = this.tip.width()
    const height = this.tip.height()
    const vert = position[1] + height > size[1] ? 'bottom' : 'top'
    const horz = position[0] + width > size[0] ? 'right' : 'left'
    this.setPositioning(`${vert}-${horz}`)
  }
}

/**
 * @desc Object with configuration options for feature tips
 * @public
 * @typedef {Object}
 * @property {ol.layer.Vector} layer The layer whose features will have tips
 * @property {module:nyc/ol/FeatureTip~FeatureTip.LabelFunction} label A function to generate tips
 */
FeatureTip.TipDef

/**
 * @desc Label function that returns a {@link module:nyc/ol/FeatureTip~FeatureTip.Label}
 * @public
 * @typedef {function(ol.Feature):module:nyc/ol/FeatureTip~FeatureTip.Label}
 */
FeatureTip.LabelFunction

/**
 * @desc Object type to return from a feature's label function
 * @public
 * @typedef {Object}
 * @property {jQuery|Element|string} html The tip content
 * @property {string=} css A CSS class to apply to the tip
 */
FeatureTip.Label

/**
 * @desc Constructor options for {@link module:nyc/ol/FeatureTip~FeatureTip}
 * @public
 * @typedef {Object}
 * @property {ol.Map} map The map
 * @property {Array<module:nyc/ol/FeatureTip~FeatureTip.TipDef>} tips The tip definitions
 */
FeatureTip.Options

/**
 * @private
 * @const
 * @type {string}
 */
FeatureTip.HTML = '<div class="f-tip" role="tooltip"></div>'

export default FeatureTip