diff --git a/assets/css/maptalks-control.css b/assets/css/maptalks-control.css index e81cfacd9..427c41857 100644 --- a/assets/css/maptalks-control.css +++ b/assets/css/maptalks-control.css @@ -75,12 +75,6 @@ padding-left: 1em; list-style: none; } -.maptalks-layer-switcher li { - padding-top: 5px; -} -.maptalks-layer-switcher .group > label { - font-weight: bold; -} .maptalks-layer-switcher .group + .group { padding-top: 1em; } @@ -88,14 +82,9 @@ text-overflow: ellipsis; overflow: hidden; display: inline-block; + font-size : 14px; white-space: nowrap; } -.maptalks-layer-switcher .group label { - width: 260px; -} -.maptalks-layer-switcher .layer label { - width: 215px; -} .maptalks-layer-switcher input { margin: 0 5px; vertical-align: text-top; @@ -113,31 +102,36 @@ width: 28px; height: 28px; background: url(images/control/layer.png) no-repeat 4px 4px; - background-color: #2f2f2f; + background-color: #172029; border: none; } .maptalks-layer-switcher.shown button { display: none; } .maptalks-layer-switcher .panel { - background-color: #2f2f2f; + background-color: #172029; display: none; - max-height: 100%; overflow-y: auto; - width: 300px; - max-height: 500px; - color: #b1b1b1; + min-width: 120px; + max-width: 400px; + max-height: 600px; + color: #fff; +} +.maptalks-layer-switcher li { + overflow:hidden; + margin-right: 1em; + white-space:nowrap; } .maptalks-layer-switcher.shown .panel { display: block; } .maptalks-layer-switcher ::-webkit-scrollbar { - width: 10px; + width: 6px; } .maptalks-layer-switcher ::-webkit-scrollbar-track { background-color: #1f1f1f; } -.maptalks-layer-switcher ::-webkit-scrollbar-thumb { +.maptalks-layer-switcher ::-webkit-scrollbar-thumb { border-radius: 5px; background-color: #777; } diff --git a/src/control/Control.LayerSwitcher.js b/src/control/Control.LayerSwitcher.js index c72b6b6f5..125e45bf6 100644 --- a/src/control/Control.LayerSwitcher.js +++ b/src/control/Control.LayerSwitcher.js @@ -1,180 +1,177 @@ -import { on, off, createEl } from 'core/util/dom'; -import Map from 'map/Map'; -import Control from './Control'; - -/** - * @property {Object} options - options - * @property {Object} [options.position='top-right'] - position of the control - * @property {Object} [options.baseTitle='Base Layers'] - title of the base layers - * @property {Object} [options.overlayTitle='Overlay Layers'] - title of the overlay layers - * @property {Object} [options.excludeLayers=[] - ids of layers that don't display in layerswitcher - * @property {Object} [options.containerClass=maptalks-layer-switcher] - layerswitcher's container div's CSS class - * - * @memberOf control.LayerSwitcher - * @instance - */ -const options = { - 'position' : 'top-right', - 'baseTitle' : 'Base Layers', - 'overlayTitle' : 'Overlay Layers', - 'excludeLayers' : [], - 'containerClass' : 'maptalks-layer-switcher' -}; - -/** - * @classdesc - * A layerswither control for the map. - * @category control - * @extends control.Control - * @memberOf control - * @example - * var layerswither = new Layerswither({ - * position : {'top': '0', 'right': '0'} - * }).addTo(map); -*/ -class LayerSwitcher extends Control { - /** - * method to build DOM of the control - * @return {HTMLDOMElement} - */ - buildOn() { - const container = this.container = createEl('div', this.options['containerClass']), - panel = this.panel = createEl('div', 'panel'), - button = this.button = createEl('button'); - container.appendChild(button); - container.appendChild(panel); - return container; - } - - onAdd() { - on(this.button, 'mouseover', this._show, this); - on(this.panel, 'mouseout', this._hide, this); - } - - onRemove() { - if (this.panel) { - this.panel.remove(); - delete this.panel; - } - off(this.button, 'mouseover', this._show, this); - off(this.panel, 'mouseout', this._hide, this); - } - - _show() { - const list = this.container.classList; - if (!list.contains('shown')) { - list.add('shown'); - this._createPanel(); - } - } - - _hide(e) { - e = e || window.event; - const list = this.container.classList; - if (!this.panel.contains(e.toElement || e.relatedTarget) && list.contains('shown')) { - list.remove('shown'); - } - } - - _createPanel() { - this.panel.innerHTML = ''; - const ul = createEl('ul'); - this.panel.appendChild(ul); - this._renderLayers(this.getMap(), ul); - } - - _renderLayers(map, elm) { - const base = map.getBaseLayer(), - layers = map.getLayers(), - len = layers.length; - if (base) { - const baseLayers = base.layers || [base], - li = createEl('li', 'group'), - ul = createEl('ul'), - label = createEl('label'); - label.innerHTML = this.options['baseTitle']; - li.appendChild(label); - for (let i = 0, len = baseLayers.length; i < len; i++) { - const layer = baseLayers[i]; - if (this._isDisplay(layer)) { - ul.appendChild(this._renderLayer(baseLayers[i], true)); - li.appendChild(ul); - elm.appendChild(li); - } - } - } - - if (len) { - const li = createEl('li', 'group'), - ul = createEl('ul'), - label = createEl('label'); - label.innerHTML = this.options['overlayTitle']; - li.appendChild(label); - for (let i = 0; i < len; i++) { - const layer = layers[i]; - if (this._isDisplay(layer)) { - ul.appendChild(this._renderLayer(layer)); - } - } - li.appendChild(ul); - elm.appendChild(li); - } - } - - _isDisplay(layer) { - const id = layer.getId(), - excludeLayers = this.options['excludeLayers']; - return !(excludeLayers.length && excludeLayers.includes(id)); - } - - _renderLayer(layer, isBase) { - const li = createEl('li', 'layer'), - label = createEl('label'), - input = createEl('input'), - map = this.getMap(), - visible = layer.isVisible(); - li.className = 'layer'; - if (isBase) { - input.type = 'radio'; - input.name = 'base'; - } else { - input.type = 'checkbox'; - } - - input.checked = visible; - if (!visible) { - input.setAttribute('disabled', 'disabled'); - } - - input.onchange = function (e) { - if (e.target.type === 'radio') { - const baseLayers = map.getBaseLayer().layers; - for (let i = 0, len = baseLayers.length; i < len; i++) { - const baseLayer = baseLayers[i]; - baseLayer[baseLayer === layer ? 'show' : 'hide'](); - } - } else { - layer[e.target.checked ? 'show' : 'hide'](); - } - }; - li.appendChild(input); - label.innerHTML = layer.getId(); - li.appendChild(label); - return li; - } -} - -LayerSwitcher.mergeOptions(options); - -Map.mergeOptions({ - 'layerSwitcherControl': false -}); - -Map.addOnLoadHook(function () { - if (this.options['layerSwitcherControl']) { - this.layerSwitcherControl = new LayerSwitcher(this.options['layerSwitcherControl']); - this.addControl(this.layerSwitcherControl); - } -}); - -export default LayerSwitcher; +import { on, off, createEl, removeDomNode, addClass, hasClass, setClass } from 'core/util/dom'; +import Map from 'map/Map'; +import Control from './Control'; + +/** + * @property {Object} options - options + * @property {Object} [options.position='top-right'] - position of the control + * @property {Object} [options.baseTitle='Base Layers'] - title of the base layers + * @property {Object} [options.overlayTitle='Overlay Layers'] - title of the overlay layers + * @property {Object} [options.excludeLayers=[] - ids of layers that don't display in layerswitcher + * @property {Object} [options.containerClass=maptalks-layer-switcher] - layerswitcher's container div's CSS class + * + * @memberOf control.LayerSwitcher + * @instance + */ +const options = { + 'position' : 'top-right', + 'baseTitle' : 'Base Layers', + 'overlayTitle' : 'Overlay Layers', + 'excludeLayers' : [], + 'containerClass' : 'maptalks-layer-switcher' +}; + +/** + * @classdesc + * A layerswither control for the map. + * @category control + * @extends control.Control + * @memberOf control + * @example + * var layerswither = new Layerswither({ + * position : {'top': '0', 'right': '0'} + * }).addTo(map); +*/ +class LayerSwitcher extends Control { + /** + * method to build DOM of the control + * @return {HTMLDOMElement} + */ + buildOn() { + const container = this.container = createEl('div', this.options['containerClass']), + panel = this.panel = createEl('div', 'panel'), + button = this.button = createEl('button'); + container.appendChild(button); + container.appendChild(panel); + return container; + } + + onAdd() { + on(this.button, 'mouseover', this._show, this); + on(this.panel, 'mouseleave', this._hide, this); + } + + onRemove() { + if (this.panel) { + off(this.button, 'mouseover', this._show, this); + off(this.panel, 'mouseleave', this._hide, this); + removeDomNode(this.panel); + delete this.panel; + delete this.button; + delete this.container; + } + } + + _show() { + if (!hasClass(this.container, 'shown')) { + addClass(this.container, 'shown'); + this._createPanel(); + } + } + + _hide() { + setClass(this.container, this.options['containerClass']); + } + + _createPanel() { + this.panel.innerHTML = ''; + const ul = createEl('ul'); + this.panel.appendChild(ul); + this._renderLayers(this.getMap(), ul); + } + + _renderLayers(map, elm) { + const base = map.getBaseLayer(), + layers = map.getLayers(), + len = layers.length; + if (base) { + const baseLayers = base.layers || [base], + li = createEl('li', 'group'), + ul = createEl('ul'), + label = createEl('label'); + label.innerHTML = this.options['baseTitle']; + li.appendChild(label); + for (let i = 0, len = baseLayers.length; i < len; i++) { + const layer = baseLayers[i]; + if (this._isExcluded(layer)) { + ul.appendChild(this._renderLayer(baseLayers[i], true)); + li.appendChild(ul); + elm.appendChild(li); + } + } + } + + if (len) { + const li = createEl('li', 'group'), + ul = createEl('ul'), + label = createEl('label'); + label.innerHTML = this.options['overlayTitle']; + li.appendChild(label); + for (let i = 0; i < len; i++) { + const layer = layers[i]; + if (this._isExcluded(layer)) { + ul.appendChild(this._renderLayer(layer)); + } + } + li.appendChild(ul); + elm.appendChild(li); + } + } + + _isExcluded(layer) { + const id = layer.getId(), + excludeLayers = this.options['excludeLayers']; + return !(excludeLayers.length && excludeLayers.indexOf(id) >= 0); + } + + _renderLayer(layer, isBase) { + const li = createEl('li', 'layer'), + label = createEl('label'), + input = createEl('input'), + map = this.getMap(), + visible = layer.isVisible(); + li.className = 'layer'; + if (isBase) { + input.type = 'radio'; + input.name = 'base'; + } else { + input.type = 'checkbox'; + } + + input.checked = visible; + // if (!visible) { + // input.setAttribute('disabled', 'disabled'); + // } + + input.onchange = function (e) { + if (e.target.type === 'radio') { + const baseLayers = map.getBaseLayer().layers; + for (let i = 0, len = baseLayers.length; i < len; i++) { + const baseLayer = baseLayers[i]; + baseLayer[baseLayer === layer ? 'show' : 'hide'](); + } + } else { + layer[e.target.checked ? 'show' : 'hide'](); + } + }; + li.appendChild(input); + label.innerHTML = layer.getId(); + li.appendChild(label); + return li; + } +} + +LayerSwitcher.mergeOptions(options); + +Map.mergeOptions({ + 'layerSwitcherControl': false +}); + +Map.addOnLoadHook(function () { + if (this.options['layerSwitcherControl']) { + this.layerSwitcherControl = new LayerSwitcher(this.options['layerSwitcherControl']); + this.addControl(this.layerSwitcherControl); + } +}); + +export default LayerSwitcher; diff --git a/test/map/control/Control.LayerSwitcherSpec.js b/test/map/control/Control.LayerSwitcherSpec.js index 16b24c8ce..29221bd49 100644 --- a/test/map/control/Control.LayerSwitcherSpec.js +++ b/test/map/control/Control.LayerSwitcherSpec.js @@ -23,11 +23,6 @@ describe('Control.LayerSwitcher', function () { }); it('baseTitle and overlayTitle', function () { - var control = new maptalks.control.LayerSwitcher({ - baseTitle: 'baseTitle', - overlayTitle : 'overlayTitle' - }); - map.addControl(control); var tile1 = new maptalks.TileLayer('tile1', { urlTemplate : '/resources/tile.png' }); @@ -36,11 +31,21 @@ describe('Control.LayerSwitcher', function () { }); map.setBaseLayer(tile1); map.addLayer(tile2); - control.container.addEventListener("mouseover", function() { - var labels = document.querySelectorAll('.group > label',this); - expect(labels[0].innerText).to.eql('baseTitle'); - expect(labels[1].innerText).to.eql('overlayTitle'); + var control = new maptalks.control.LayerSwitcher({ + baseTitle: 'baseTitle', + overlayTitle : 'overlayTitle' }); + map.addControl(control); + happen.mouseover(control.button, { + 'clientX' : 100, + 'clientY' : 100 + }); + + var labels = document.querySelectorAll('.maptalks-layer-switcher label', this); + expect(labels[0].innerText).to.be.eql('baseTitle'); + expect(labels[1].innerText).to.be.eql('tile1'); + expect(labels[2].innerText).to.be.eql('overlayTitle'); + expect(labels[3].innerText).to.be.eql('tile2'); }); it('excludeLayers', function () { @@ -55,12 +60,14 @@ describe('Control.LayerSwitcher', function () { urlTemplate : '/resources/tile.png' }); map.addLayer([tile1, tile2]); - control.container.addEventListener("mouseover", function() { - var labels = document.querySelectorAll('.layer label',this); - for (var i = 0, len = labels.length; i < len; i++) { - expect(labels[i].innerText).not.to.eql('tile1'); - } + happen.mouseover(control.button, { + 'clientX' : 100, + 'clientY' : 100 }); + var labels = document.querySelectorAll('.layer label', this); + for (var i = 0, len = labels.length; i < len; i++) { + expect(labels[i].innerText).not.to.eql('tile1'); + } }); it('base layers switch', function () { @@ -76,18 +83,20 @@ describe('Control.LayerSwitcher', function () { }) ]); map.setBaseLayer(group); - control.container.addEventListener("mouseover", function() { - var radios = document.querySelectorAll('.layer input',this); - expect(radios[0].checked).to.be.ok(); - expect(radios[1].checked).not.to.be.ok(); - expect(group.layers[0].isVisible()).to.be.ok(); - expect(group.layers[1].isVisible()).not.to.be.ok(); - radios[1].addEventListener("click", function() { - expect(radios[0].checked).not.to.be.ok(); - expect(radios[1].checked).to.be.ok(); - expect(group.layers[0].isVisible()).not.to.be.ok(); - expect(group.layers[1].isVisible()).to.be.ok(); - }); + happen.mouseover(control.button, { + 'clientX' : 100, + 'clientY' : 100 + }); + var radios = document.querySelectorAll('.layer input',this); + expect(radios[0].checked).to.be.ok(); + expect(radios[1].checked).not.to.be.ok(); + expect(group.layers[0].isVisible()).to.be.ok(); + expect(group.layers[1].isVisible()).not.to.be.ok(); + radios[1].addEventListener("click", function() { + expect(radios[0].checked).not.to.be.ok(); + expect(radios[1].checked).to.be.ok(); + expect(group.layers[0].isVisible()).not.to.be.ok(); + expect(group.layers[1].isVisible()).to.be.ok(); }); }); @@ -98,14 +107,50 @@ describe('Control.LayerSwitcher', function () { urlTemplate : '/resources/tile.png' }); map.addLayer(tile1); - control.container.addEventListener("mouseover", function() { - var checkbox = document.querySelector('.layer input',this); - expect(checkbox.checked).to.be.ok(); - expect(tile1.isVisible()).to.be.ok(); - checkbox.addEventListener("click", function() { - expect(checkbox.checked).not.to.be.ok(); - expect(tile1.isVisible()).not.to.be.ok(); - }); + happen.mouseover(control.button, { + 'clientX' : 100, + 'clientY' : 100 + }); + var checkbox = document.querySelector('.layer input',this); + expect(checkbox.checked).to.be.ok(); + expect(tile1.isVisible()).to.be.ok(); + checkbox.addEventListener("click", function() { + expect(checkbox.checked).not.to.be.ok(); + expect(tile1.isVisible()).not.to.be.ok(); + }); + }); + + it('show and hide', function () { + var control = new maptalks.control.LayerSwitcher(); + map.addControl(control); + var tile1 = new maptalks.TileLayer('tile1', { + urlTemplate : '/resources/tile.png' + }); + map.addLayer(tile1); + happen.mouseover(control.button, { + 'clientX' : 100, + 'clientY' : 100 + }); + expect(control.container.className).to.be.eql(control.options.containerClass + ' shown'); + happen.once(control.panel, { + 'type' : 'mouseleave', + 'clientX' : 100, + 'clientY' : 100 + }); + expect(control.container.className).to.be.eql(control.options.containerClass); + }); + + it('remove from map', function () { + var control = new maptalks.control.LayerSwitcher(); + map.addControl(control); + var tile1 = new maptalks.TileLayer('tile1', { + urlTemplate : '/resources/tile.png' + }); + map.addLayer(tile1); + happen.mouseover(control.button, { + 'clientX' : 100, + 'clientY' : 100 }); + control.remove(); }); });