diff --git a/README.md b/README.md index 1de8bc5..2ffa3fc 100644 --- a/README.md +++ b/README.md @@ -59,6 +59,24 @@ L.control.groupedLayers(baseLayers, groupedOverlays, options).addTo(map); ![advanced preview](preview-advanced.png) +#### Collapsable Groups + +Enabling collapsable groups in the switcher can be done with the following options + +```javascript +var options = { + // enable basic collapsability + groupsCollapsable: true, + // (Optional) The css class(es) used to indicated the group is expanded + groupsExpandedClass: "glyphicon glyphicon-chevron-down", + // (Optional) The css class(es) used to indicated the group is collapsed + groupsCollapsedClass: "glyphicon glyphicon-chevron-right" +}; +L.control.groupedLayers(baseLayers, groupedOverlays, options).addTo(map); +``` + +![collapsable preview](preview-collapsable.png) + ### Adding a layer Adding a layer individually works similarly to the default layer control, diff --git a/preview-advanced.png b/preview-advanced.png index 83a4a37..c5ab210 100644 Binary files a/preview-advanced.png and b/preview-advanced.png differ diff --git a/preview.png b/preview.png index 4ce20d5..5256a7e 100644 Binary files a/preview.png and b/preview.png differ diff --git a/src/leaflet.groupedlayercontrol.css b/src/leaflet.groupedlayercontrol.css index 3be2cc4..164f1a4 100644 --- a/src/leaflet.groupedlayercontrol.css +++ b/src/leaflet.groupedlayercontrol.css @@ -1,14 +1,38 @@ .leaflet-control-layers-group-name { - font-weight: bold; - margin-bottom: .2em; + font-weight: 700; + margin-bottom: 0.2em; margin-left: 3px; } - .leaflet-control-layers-group { - margin-bottom: .5em; + margin-bottom: 0.5em; } - .leaflet-control-layers-scrollbar { overflow-y: scroll; padding-right: 10px; } +.leaflet-control-layers-group-label { + margin-bottom: 2px; +} +.leaflet-control-layers-group-selector, .leaflet-control-layers-selector { + vertical-align: top; +} +.leaflet-control-layers-group label:not(.leaflet-control-layers-group-label) { + text-indent: 15px; +} +.leaflet-control-layers-group.group-collapsable.collapsed .leaflet-control-layers-group-collapse, +.leaflet-control-layers-group.group-collapsable:not(.collapsed) .leaflet-control-layers-group-expand, +.leaflet-control-layers-group.group-collapsable.collapsed label:not(.leaflet-control-layers-group-label){ + display: none; +} +.leaflet-control-layers-group-expand-default:before{ + content: "+"; + width: 12px; + display: inline-block; + text-align: center; +} +.leaflet-control-layers-group-collapse-default:before{ + content: "-"; + width: 12px; + display: inline-block; + text-align: center; +} \ No newline at end of file diff --git a/src/leaflet.groupedlayercontrol.js b/src/leaflet.groupedlayercontrol.js index b1e80d1..68972c5 100644 --- a/src/leaflet.groupedlayercontrol.js +++ b/src/leaflet.groupedlayercontrol.js @@ -5,11 +5,26 @@ L.Control.GroupedLayers = L.Control.extend({ options: { + sortLayers: true, + sortGroups: true, + sortBaseLayers: false, collapsed: true, position: 'topright', autoZIndex: true, exclusiveGroups: [], - groupCheckboxes: false + groupCheckboxes: false, + groupsCollapsable: false, + groupsExpandedClass: "leaflet-control-layers-group-collapse-default", + groupsCollapsedClass: "leaflet-control-layers-group-expand-default", + sortFunction: function (nameA, nameB) { + if (nameA < nameB) { + return -1; + } else if (nameB < nameA) { + return 1; + } else { + return 0; + } + } }, initialize: function (baseLayers, groupedOverlays, options) { @@ -44,6 +59,12 @@ L.Control.GroupedLayers = L.Control.extend({ return this._container; }, + addTo: function (map) { + L.Control.prototype.addTo.call(this, map); + // Trigger expand after Layers Control has been inserted into DOM so that is now has an actual height. + return this._expandIfNotCollapsed(); + }, + onRemove: function (map) { map .off('layeradd', this._onLayerChange, this) @@ -66,57 +87,56 @@ L.Control.GroupedLayers = L.Control.extend({ var id = L.Util.stamp(layer); var _layer = this._getLayer(id); if (_layer) { - delete this._layers[this._layers.indexOf(_layer)]; + this._layers.splice(this._layers.indexOf(_layer), 1); } this._update(); return this; }, _getLayer: function (id) { - for (var i = 0; i < this._layers.length; i++) { - if (this._layers[i] && L.stamp(this._layers[i].layer) === id) { - return this._layers[i]; + for (var layer of this._layers) { + if (layer && L.stamp(layer.layer) === id) { + return layer; } } }, _initLayout: function () { var className = 'leaflet-control-layers', - container = this._container = L.DomUtil.create('div', className); - + container = this._container = L.DomUtil.create('div', className), + collapsed = this.options.collapsed; + // Makes this work on IE10 Touch devices by stopping it from firing a mouseout event when the touch is released container.setAttribute('aria-haspopup', true); - if (L.Browser.touch) { - L.DomEvent.on(container, 'click', L.DomEvent.stopPropagation); - } else { - L.DomEvent.disableClickPropagation(container); - L.DomEvent.on(container, 'wheel', L.DomEvent.stopPropagation); - } + L.DomEvent.disableClickPropagation(container); + L.DomEvent.disableScrollPropagation(container); var form = this._form = L.DomUtil.create('form', className + '-list'); - if (this.options.collapsed) { + if (collapsed) { + this._map.on('click', this._collapse, this); + if (!L.Browser.android) { - L.DomEvent - .on(container, 'mouseover', this._expand, this) - .on(container, 'mouseout', this._collapse, this); - } - var link = this._layersLink = L.DomUtil.create('a', className + '-toggle', container); - link.href = '#'; - link.title = 'Layers'; - - if (L.Browser.touch) { - L.DomEvent - .on(link, 'click', L.DomEvent.stop) - .on(link, 'click', this._expand, this); - } else { - L.DomEvent.on(link, 'focus', this._expand, this); + L.DomEvent.on(container, { + mouseenter: this._expand, + mouseleave: this._collapse + }, this); } + } - this._map.on('click', this._collapse, this); - // TODO keyboard accessibility + var link = this._layersLink = L.DomUtil.create('a', className + '-toggle', container); + link.href = '#'; + link.title = 'Layers'; + + if (L.Browser.touch) { + L.DomEvent.on(link, 'click', L.DomEvent.stop); + L.DomEvent.on(link, 'click', this._expand, this); } else { + L.DomEvent.on(link, 'focus', this._expand, this); + } + + if (!collapsed) { this._expand(); } @@ -128,7 +148,6 @@ L.Control.GroupedLayers = L.Control.extend({ }, _addLayer: function (layer, name, group, overlay) { - var id = L.Util.stamp(layer); var _layer = { layer: layer, @@ -156,6 +175,30 @@ L.Control.GroupedLayers = L.Control.extend({ this._lastZIndex++; layer.setZIndex(this._lastZIndex); } + + if (this.options.sortLayers) { + this._layers.sort(L.bind(function (a, b) { + if (a.overlay == true && b.overlay == true) { + return this.options.sortFunction(a.name, b.name); + } + }, this)); + } + + if (this.options.sortBaseLayers) { + this._layers.sort(L.bind(function (a, b) { + if (a.overlay == undefined && b.overlay == undefined) { + return this.options.sortFunction(a.name, b.name); + } + }, this)); + } + + if (this.options.sortGroups) { + this._layers.sort(L.bind(function (a, b) { + return this.options.sortFunction(a.group.name, b.group.name); + }, this)); + } + + this._expandIfNotCollapsed(); }, _update: function () { @@ -168,16 +211,18 @@ L.Control.GroupedLayers = L.Control.extend({ this._domGroups.length = 0; var baseLayersPresent = false, - overlaysPresent = false, - i, obj; + overlaysPresent = false; - for (var i = 0; i < this._layers.length; i++) { - obj = this._layers[i]; + for (var obj of this._layers) { this._addItem(obj); overlaysPresent = overlaysPresent || obj.overlay; baseLayersPresent = baseLayersPresent || !obj.overlay; } + if (this.options.groupCheckboxes) { + this._refreshGroupsCheckStates(); + } + this._separator.style.display = overlaysPresent && baseLayersPresent ? '' : 'none'; }, @@ -204,18 +249,14 @@ L.Control.GroupedLayers = L.Control.extend({ } }, - // IE7 bugs out if you create a radio dynamically, so you have to do it this hacky way (see http://bit.ly/PqYLBe) _createRadioElement: function (name, checked) { - var radioHtml = '