Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .nvmrc
Original file line number Diff line number Diff line change
@@ -1 +1 @@
15.11.0
22
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@
"grunt-prettier": "^2.2.0",
"leaflet": "^1.9.4",
"leaflet.locatecontrol": "^0.81.1",
"mapml-extension": "git+https://github.com/Maps4HTML/mapml-extension",
"mapml-extension": "github:Maps4HTML/mapml-extension#main",
"media-query-parser": "^3.0.2",
"media-query-solver": "^0.1.3",
"path": "^0.12.7",
Expand Down
1 change: 0 additions & 1 deletion src/mapml-viewer.js
Original file line number Diff line number Diff line change
Expand Up @@ -481,7 +481,6 @@ export class HTMLMapmlViewerElement extends HTMLElement {
collapsed: true,
mapEl: this
}).addTo(this._map);
this._map.on('movestart', this._layerControl.collapse, this._layerControl);

let scaleValue = M.options.announceScale;

Expand Down
54 changes: 50 additions & 4 deletions src/mapml.css
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,19 @@ the browser)' */
color: revert;
}

.mapml-layer-item-legend-link {
display: inline-block;
margin-block-start: .25rem;
}

.mapml-layer-item-legend-image {
display: block;
max-width: min(100%, 16rem);
height: auto;
border: 1px solid #e3e3e3;
border-radius: 2px;
}

.leaflet-top .leaflet-control {
margin-top: 5px;
}
Expand Down Expand Up @@ -277,9 +290,6 @@ and: https://developer.mozilla.org/en-US/docs/Web/CSS/:fullscreen */
.leaflet-control-layers fieldset {
margin: 0;
padding: 0;
/* Invisible (white) default fieldset border adds ~1.6px, shifting controls
below when a single-layer control expands on hover; remove it. */
border: 0;
min-height: 44px;
}

Expand Down Expand Up @@ -307,6 +317,42 @@ and: https://developer.mozilla.org/en-US/docs/Web/CSS/:fullscreen */
background-size: 34px;
}

/* When the panel is expanded, split the visual "box" into two: the toggle
icon keeps its own bordered white square (matching its collapsed look),
and the layer list becomes a separate bordered white box below it. The
container itself becomes a transparent layout wrapper. pointer-events:
none lets map drag/pan pass through the empty area beside the toggle
and beside/above the expanded list; children re-enable pointer-events. */
.leaflet-control-layers-expanded {
background: transparent !important;
border: 0 !important;
box-shadow: none !important;
padding: 0 !important;
pointer-events: none;
}

.leaflet-control-layers-expanded .leaflet-control-layers-toggle {
display: block;
margin-left: auto;
background-color: #fff;
border: 1px solid #e3e3e3;
border-radius: 4px;
box-shadow: rgb(0 0 0 / 30%) 0px 1px 4px -1px;
pointer-events: auto;
}

.leaflet-control-layers-expanded .leaflet-control-layers-list {
display: block;
position: relative;
margin-top: 1px;
background-color: #fff;
border: 1px solid #e3e3e3;
border-radius: 4px;
box-shadow: rgb(0 0 0 / 30%) 0px 1px 4px -1px;
overflow: hidden;
pointer-events: auto;
}

/* Revert Leaflet styles that are causing misalignment. */
.leaflet-control-layers-selector {
margin-top: revert;
Expand Down Expand Up @@ -804,7 +850,7 @@ label.mapml-layer-item-toggle {
padding-block-start: .25rem;
padding-block-end: .25rem;
padding-inline-start: .25rem;
padding-inline-end: 1rem;
display: inline-block;
}

.mapml-layer-item-settings > * {
Expand Down
120 changes: 48 additions & 72 deletions src/mapml/control/LayerControl.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,34 +29,37 @@ export var LayerControl = Control.Layers.extend({
},
onAdd: function () {
this._initLayout();
// Adding event on layer control button
// Remove Leaflet's auto-open/close listeners: the control now opens and
// closes only via clicking the toggle icon.
DomEvent.off(this._container, 'mouseenter', this._expandSafely, this);
DomEvent.off(this._container, 'mouseleave', this.collapse, this);
this._map.off('click', this.collapse, this);

// Replace the toggle anchor with a clone to strip Leaflet's built-in
// expand-only click/keydown handlers, then wire toggle behaviour.
const originalLink = this._layersLink;
const link = originalLink.cloneNode(true);
originalLink.parentNode.replaceChild(link, originalLink);
this._layersLink = link;
DomEvent.on(
this._container.getElementsByTagName('a')[0],
'keydown',
this._focusFirstLayer,
this._container
);
// Suppress synthetic mouse events on touch-expand so the browser
// can't retarget the ghost click to the settings gear button.
DomEvent.on(
this._container.getElementsByTagName('a')[0],
'touchend',
this._expandOnTouch,
link,
{
click: function (e) {
DomEvent.preventDefault(e);
this._toggle();
},
keydown: function (e) {
if (e.keyCode === 13) {
DomEvent.preventDefault(e);
this._toggle();
}
}
},
this
);
// Collapse on any touch outside the control; movestart alone is
// unreliable because a plain tap doesn't pan the map.
this._outsideTouchHandler = (e) => {
if (!this._container.contains(e.target)) {
this._container._isExpanded = false;
this.collapse(e);
}
};
this._map
.getContainer()
.addEventListener('touchstart', this._outsideTouchHandler, {
passive: true
});

// Adding event on layer control button
DomEvent.on(link, 'keydown', this._focusFirstLayer, this._container);
DomEvent.on(
this._container,
'contextmenu',
Expand All @@ -73,23 +76,11 @@ export var LayerControl = Control.Layers.extend({
},
onRemove: function (map) {
DomEvent.off(
this._container.getElementsByTagName('a')[0],
this._layersLink,
'keydown',
this._focusFirstLayer,
this._container
);
DomEvent.off(
this._container.getElementsByTagName('a')[0],
'touchend',
this._expandOnTouch,
this
);
if (this._outsideTouchHandler) {
map
.getContainer()
.removeEventListener('touchstart', this._outsideTouchHandler);
this._outsideTouchHandler = null;
}
},
addOrUpdateOverlay: function (layer, name) {
var alreadyThere = false;
Expand Down Expand Up @@ -123,12 +114,11 @@ export var LayerControl = Control.Layers.extend({
_focusFirstLayer: function (e) {
if (
e.key === 'Enter' &&
this.className ===
'leaflet-control-layers leaflet-control leaflet-control-layers-expanded'
this.classList.contains('leaflet-control-layers-expanded')
) {
var elem =
this.children[1].children[2].children[0].children[0].children[0]
.children[0];
var elem = this.querySelector(
'.leaflet-control-layers-overlays input.leaflet-control-layers-selector'
);
if (elem) setTimeout(() => elem.focus(), 0);
}
},
Expand Down Expand Up @@ -193,40 +183,26 @@ export var LayerControl = Control.Layers.extend({
return layercontrols;
},

//overrides collapse and conditionally collapses the panel
collapse: function (e) {
// if layer control is not expanded, return
if (!this._container.className.includes('expanded')) {
return;
}
// return if layer contextmenu is still open
if (
!this._map.contextMenu._extentLayerMenu.hidden ||
!this._map.contextMenu._layerMenu.hidden
) {
return;
}
if (
e.target.tagName === 'SELECT' ||
(e.relatedTarget &&
e.relatedTarget.parentElement &&
(e.relatedTarget.className === 'mapml-contextmenu mapml-layer-menu' ||
e.relatedTarget.parentElement.className ===
'mapml-contextmenu mapml-layer-menu')) ||
(this._map && this._map.contextMenu._layerMenu.style.display === 'block')
)
return this;

// Only the close button (or programmatic callers) should collapse the panel.
collapse: function () {
DomUtil.removeClass(this._container, 'leaflet-control-layers-expanded');
if (e.originalEvent?.pointerType === 'touch') {
this._container._isExpanded = false;
this._container._isExpanded = false;
return this;
},
_toggle: function () {
if (DomUtil.hasClass(this._container, 'leaflet-control-layers-expanded')) {
this.collapse();
} else {
this._expandSafely();
}
return this;
},
_expandOnTouch: function (e) {
DomEvent.preventDefault(e);
// Track expanded state so touch-device logic in _preventDefaultContextMenu
// and any callers can consult _isExpanded uniformly.
expand: function () {
Control.Layers.prototype.expand.call(this);
this._container._isExpanded = true;
this.expand();
return this;
},
_preventDefaultContextMenu: function (e) {
let latlng = this._map.mouseEventToLatLng(e);
Expand Down
31 changes: 28 additions & 3 deletions src/mapml/elementSupport/layers/createLayerControlForLayer.js
Original file line number Diff line number Diff line change
Expand Up @@ -145,13 +145,38 @@ export var createLayerControlHTML = async function () {
// to `.href` here. `rel="noopener noreferrer"` closes the
// reverse-tabnabbing and referrer-leak side channels for the
// author-supplied cross-origin destination.
var legendLink = document.createElement('a');
legendLink.text = ' ' + this._layer._title;
layerItemName.innerText = this._layer._title;

let legendControl = DomUtil.create(
'details',
'mapml-layer-item-legend mapml-control-layers',
layerItemSettings
),
legendSummary = DomUtil.create('summary'),
legendLink = document.createElement('a'),
legendImage = document.createElement('img');

legendSummary.innerText = mapEl.locale.lmLegend;
legendControl.appendChild(legendSummary);

legendLink.href = this._layer._legendUrl;
legendLink.target = '_blank';
legendLink.rel = 'noopener noreferrer';
legendLink.draggable = false;
layerItemName.appendChild(legendLink);
legendLink.className = 'mapml-layer-item-legend-link';

legendImage.src = this._layer._legendUrl;
legendImage.alt = `${this._layer._title} ${mapEl.locale.lmLegend}`;
legendImage.loading = 'lazy';
legendImage.decoding = 'async';
legendImage.className = 'mapml-layer-item-legend-image';
legendImage.addEventListener('error', () => {
legendLink.textContent = mapEl.locale.lmOpenInNewTab;
legendImage.remove();
});

legendLink.appendChild(legendImage);
legendControl.appendChild(legendLink);
} else {
// textContent (not innerHTML) so that a malicious layer title
// cannot inject markup into the layer control.
Expand Down
1 change: 0 additions & 1 deletion src/web-map.js
Original file line number Diff line number Diff line change
Expand Up @@ -523,7 +523,6 @@ export class HTMLWebMapElement extends HTMLMapElement {
collapsed: true,
mapEl: this
}).addTo(this._map);
this._map.on('movestart', this._layerControl.collapse, this._layerControl);

let scaleValue = M.options.announceScale;

Expand Down
60 changes: 60 additions & 0 deletions test/e2e/layers/layerLegend.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
<!DOCTYPE html>
<html lang="en">

<head>
<title>Layer Legend Control Tests</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script type="module" src="mapml.js"></script>
<style>
html,
body {
height: 100%;
}

* {
margin: 0;
padding: 0;
}

mapml-viewer:defined {
max-width: 100%;
width: 100%;
height: 100%;
}

mapml-viewer:not(:defined) > * {
display: none;
}

map-layer {
display: none;
}
</style>
</head>

<body>
<mapml-viewer projection="CBMTILE" zoom="2" lat="45" lon="-95" controls>
<map-layer label="Toporama" src="data/templatedImage.mapml" checked></map-layer>

<map-layer label="Inline No Legend" checked>
<map-meta name="zoom" content="min=0,max=3,value=2"></map-meta>
<map-meta name="projection" content="CBMTILE"></map-meta>
<map-tile zoom="2" row="10" col="11" src="data/cbmt/2/c11_r10.png"></map-tile>
</map-layer>

<map-layer label="html legend" checked>
<map-meta name="zoom" content="min=0,max=3,value=2"></map-meta>
<map-meta name="projection" content="CBMTILE"></map-meta>
<map-link rel="legend" href="https://maps4html.org/web-map-doc/"></map-link>
<map-feature>
<map-geometry cs="gcrs">
<map-point class="ottawa">
<map-coordinates>-75.697193 45.421530</map-coordinates>
</map-point>
</map-geometry>
</map-feature>
</mapml-viewer>
</body>

</html>
Loading
Loading