UNPKG

leaflet-styleeditor

Version:

Edit the style of features drawn within Leaflet.

428 lines (352 loc) 13.1 kB
import 'leaflet' export default function setupControl () { L.Control.StyleEditor = L.Control.extend({ options: { position: 'topleft', colorRamp: ['#1abc9c', '#2ecc71', '#3498db', '#9b59b6', '#34495e', '#16a085', '#27ae60', '#2980b9', '#8e44ad', '#2c3e50', '#f1c40f', '#e67e22', '#e74c3c', '#ecf0f1', '#95a5a6', '#f39c12', '#d35400', '#c0392b', '#bdc3c7', '#7f8c8d'], defaultColor: null, markerType: L.StyleEditor.marker.DefaultMarker, markers: null, defaultMarkerIcon: null, defaultMarkerColor: null, geometryForm: L.StyleEditor.forms.GeometryForm, ignoreLayerTypes: [], forms: {}, openOnLeafletDraw: true, openOnLeafletEditable: true, showTooltip: true, strings: { cancel: 'Cancel', cancelTitle: 'Cancel Styling', tooltip: 'Click on the element you want to style', tooltipNext: 'Choose another element you want to style' }, useGrouping: true, styleEditorEventPrefix: 'styleeditor:', // internal currentElement: null, _editLayers: [], _layerGroups: [] }, initialize: function (options) { if (options) { L.setOptions(this, options) } this.options.util = new L.StyleEditor.Util({styleEditorOptions: this.options}) // eslint-disable-next-line new-cap this.options.markerType = new this.options.markerType({styleEditorOptions: this.options}) // eslint-disable-next-line new-cap this.options.markerForm = new this.options.markerType.markerForm({styleEditorOptions: this.options}) // eslint-disable-next-line new-cap this.options.geometryForm = new this.options.geometryForm({styleEditorOptions: this.options}) this.getDefaultIcon = this.options.markerType._createMarkerIcon.bind(this.options.markerType) this.createIcon = this.options.markerType.createMarkerIcon.bind(this.options.markerType) }, onAdd: function (map) { this.options.map = map return this.createUi() }, fireEvent: function (eventName, element) { this.options.util.fireEvent(eventName, element) }, createUi: function () { let controlDiv = this.options.controlDiv = L.DomUtil.create('div', 'leaflet-control-styleeditor leaflet-control leaflet-bar') let controlUI = this.options.controlUI = L.DomUtil.create('a', 'leaflet-control-styleeditor-interior', controlDiv) controlUI.title = 'Style Editor' let cancel = this.options.cancelUI = L.DomUtil.create('div', 'leaflet-control-styleeditor-cancel leaflet-styleeditor-hidden', controlDiv) cancel.innerHTML = this.options.strings.cancel cancel.title = this.options.strings.cancelTitle let styleEditorDiv = this.options.styleEditorDiv = L.DomUtil.create('div', 'leaflet-styleeditor', this.options.map._container) this.options.styleEditorHeader = L.DomUtil.create('div', 'leaflet-styleeditor-header', styleEditorDiv) let styleEditorInterior = L.DomUtil.create('div', 'leaflet-styleeditor-interior', styleEditorDiv) this.addDomEvents() this.addEventListeners() this.addButtons() this.options.styleForm = new L.StyleForm({ styleEditorDiv: styleEditorDiv, styleEditorInterior: styleEditorInterior, styleEditorOptions: this.options }) return controlDiv }, addDomEvents: function () { L.DomEvent.disableScrollPropagation(this.options.styleEditorDiv) L.DomEvent.disableScrollPropagation(this.options.controlDiv) L.DomEvent.disableScrollPropagation(this.options.cancelUI) L.DomEvent.disableClickPropagation(this.options.styleEditorDiv) L.DomEvent.disableClickPropagation(this.options.controlDiv) L.DomEvent.disableClickPropagation(this.options.cancelUI) L.DomEvent.on(this.options.controlDiv, 'click', function () { this.toggle() }, this) }, addEventListeners: function () { this.addLeafletDrawEvents() this.addLeafletEditableEvents() }, addLeafletDrawEvents: function () { if (!this.options.openOnLeafletDraw || !L.Control.Draw) { return } this.options.map.on('layeradd', this.onLayerAdd, this) this.options.map.on(L.Draw.Event.CREATED, this.onLayerCreated, this) }, addLeafletEditableEvents: function () { if (!this.options.openOnLeafletEditable || !L.Editable) { return } this.options.map.on('layeradd', this.onLayerAdd, this) this.options.map.on('editable:created', this.onLayerCreated, this) }, onLayerCreated: function (layer) { this.removeIndicators() this.options.currentElement = layer.layer }, onLayerAdd: function (e) { if (this.options.currentElement) { if (e.layer === this.options.util.getCurrentElement()) { this.enable(e.layer) } } }, onRemove: function () { // hide everything that may be visible // remove edit events for layers // remove tooltip this.disable() // remove events this.removeDomEvents() this.removeEventListeners() // remove dom elements L.DomUtil.remove(this.options.styleEditorDiv) L.DomUtil.remove(this.options.cancelUI) // delete dom elements delete this.options.styleEditorDiv delete this.options.cancelUI }, removeEventListeners: function () { this.options.map.off('layeradd', this.onLayerAdd) if (L.Draw) { this.options.map.off(L.Draw.Event.CREATED, this.onLayerCreated) } if (L.Editable) { this.options.map.off('editable:created', this.onLayerCreated) } }, removeDomEvents: function () { L.DomEvent.off(this.options.controlDiv, 'click', function () { this.toggle() }, this) }, addButtons: function () { let nextBtn = L.DomUtil.create('button', 'leaflet-styleeditor-button styleeditor-nextBtn', this.options.styleEditorHeader) nextBtn.title = this.options.strings.tooltipNext L.DomEvent.on(nextBtn, 'click', function (e) { e.preventDefault() // Prevent form submit this.hideEditor() if (L.DomUtil.hasClass(this.options.controlUI, 'enabled')) { this.createTooltip() } e.stopPropagation() }, this) }, toggle: function () { if (L.DomUtil.hasClass(this.options.controlUI, 'enabled')) { this.disable() } else { this.enable() } }, enable: function (layer) { if (this._layerIsIgnored(layer)) { return } L.DomUtil.addClass(this.options.controlUI, 'enabled') this.options.map.eachLayer(this.addEditClickEvents, this) this.showCancelButton() this.createTooltip() if (layer !== undefined) { if (this.isEnabled()) { this.removeIndicators() } this.initChangeStyle({target: layer}) } }, isEnabled: function () { return L.DomUtil.hasClass(this.options.controlUI, 'enabled') }, disable: function () { if (this.isEnabled()) { this.options._editLayers.forEach(this.removeEditClickEvents, this) this.options._editLayers = [] this.options._layerGroups = [] this.hideEditor() this.hideCancelButton() this.removeTooltip() L.DomUtil.removeClass(this.options.controlUI, 'enabled') } }, addEditClickEvents: function (layer) { if (this._layerIsIgnored(layer)) { return } if (this.options.useGrouping && layer instanceof L.LayerGroup) { this.options._layerGroups.push(layer) } else if (layer instanceof L.Marker || layer instanceof L.Path) { let evt = layer.on('click', this.initChangeStyle, this) this.options._editLayers.push(evt) } }, removeEditClickEvents: function (layer) { layer.off('click', this.initChangeStyle, this) }, addIndicators: function () { if (!this.options.currentElement) { return } let currentElement = this.options.currentElement.target if (currentElement instanceof L.LayerGroup) { currentElement.eachLayer(function (layer) { if (layer instanceof L.Marker && layer.getElement()) { L.DomUtil.addClass(layer.getElement(), 'leaflet-styleeditor-marker-selected') } }) } else if (currentElement instanceof L.Marker) { if (currentElement.getElement()) { L.DomUtil.addClass(currentElement.getElement(), 'leaflet-styleeditor-marker-selected') } } }, removeIndicators: function () { if (!this.options.currentElement) { return } let currentElement = this.options.util.getCurrentElement() if (currentElement instanceof L.LayerGroup) { currentElement.eachLayer(function (layer) { if (layer.getElement()) { L.DomUtil.removeClass(layer.getElement(), 'leaflet-styleeditor-marker-selected') } }) } else { if (currentElement.getElement()) { L.DomUtil.removeClass(currentElement.getElement(), 'leaflet-styleeditor-marker-selected') } } }, hideEditor: function () { if (L.DomUtil.hasClass(this.options.styleEditorDiv, 'editor-enabled')) { this.removeIndicators() L.DomUtil.removeClass(this.options.styleEditorDiv, 'editor-enabled') this.fireEvent('hidden') } }, hideCancelButton: function () { L.DomUtil.addClass(this.options.cancelUI, 'leaflet-styleeditor-hidden') }, showEditor: function () { let editorDiv = this.options.styleEditorDiv if (!L.DomUtil.hasClass(editorDiv, 'editor-enabled')) { L.DomUtil.addClass(editorDiv, 'editor-enabled') this.fireEvent('visible') } }, showCancelButton: function () { L.DomUtil.removeClass(this.options.cancelUI, 'leaflet-styleeditor-hidden') }, initChangeStyle: function (e) { this.removeIndicators() this.options.currentElement = (this.options.useGrouping) ? this.getMatchingElement(e) : e this.addIndicators() this.showEditor() this.removeTooltip() let layer = e if (!(layer instanceof L.Layer)) { layer = e.target } this.fireEvent('editing', layer) if (layer instanceof L.Marker) { // ensure iconOptions are set for Leaflet.Draw created Markers this.options.markerType.resetIconOptions() // marker this.showMarkerForm(layer) } else { // layer with of type L.GeoJSON or L.Path (polyline, polygon, ...) this.showGeometryForm(layer) } }, showGeometryForm: function (layer) { this.fireEvent('geometry', layer) this.options.styleForm.showGeometryForm() }, showMarkerForm: function (layer) { this.fireEvent('marker', layer) this.options.styleForm.showMarkerForm() }, createTooltip: function () { if (!this.options.showTooltip) { return } if (!this.options.tooltipWrapper) { this.options.tooltipWrapper = L.DomUtil.create('div', 'leaflet-styleeditor-tooltip-wrapper', this.options.map.getContainer()) } if (!this.options.tooltip) { this.options.tooltip = L.DomUtil.create('div', 'leaflet-styleeditor-tooltip', this.options.tooltipWrapper) } this.options.tooltip.innerHTML = this.options.strings.tooltip }, getMatchingElement: function (e) { let group = null let layer = e.target for (let i = 0; i < this.options._layerGroups.length; ++i) { group = this.options._layerGroups[i] if (group && layer !== group && group.hasLayer(layer)) { // we use the opacity style to check for correct object if (!group.options || !group.options.opacity) { group.options = layer.options // special handling for layers... we pass the setIcon function if (layer.setIcon) { group.setIcon = function (icon) { group.eachLayer(function (layer) { if (layer instanceof L.Marker) { layer.setIcon(icon) } }) } } } return this.getMatchingElement({ target: group }) } } return e }, removeTooltip: function () { if (this.options.tooltip && this.options.tooltip.parentNode) { this.options.tooltip.remove() this.options.tooltip = undefined } }, _layerIsIgnored: function (layer) { if (layer === undefined) { return false } return this.options.ignoreLayerTypes.some( layerType => layer.styleEditor && layer.styleEditor.type.toUpperCase() === layerType.toUpperCase() ) } }) L.control.styleEditor = function (options) { if (!options) { options = {} } return new L.Control.StyleEditor(options) } }