diff --git a/src/mapml-viewer.js b/src/mapml-viewer.js
index 6fc174173..6157f82f0 100644
--- a/src/mapml-viewer.js
+++ b/src/mapml-viewer.js
@@ -265,8 +265,6 @@ export class MapViewer extends HTMLElement {
fadeAnimation: true
});
this._addToHistory();
- // the attribution control is not optional
- M.attributionControl(this);
this._createControls();
this._toggleControls();
diff --git a/src/mapml.css b/src/mapml.css
index 26586ebe2..6091526c8 100644
--- a/src/mapml.css
+++ b/src/mapml.css
@@ -153,13 +153,9 @@
.leaflet-tooltip-pane .leaflet-tooltip,
.leaflet-touch .leaflet-control-layers,
.leaflet-touch .leaflet-bar,
-.leaflet-popup-tip {
- box-shadow: 0 1px 3px rgba(0, 0, 0, 0.2), 0 1px 2px rgb(0, 0, 0, 0.3);
-}
-
-/* Increase contrast between the attribution container and the underlying content. */
+.leaflet-popup-tip,
.leaflet-container .leaflet-control-attribution {
- background-color: rgba(255, 255, 255, 0.95);
+ box-shadow: rgb(0 0 0 / 30%) 0px 1px 4px -1px;
}
/* Remove opinionated styles of attribution links. */
@@ -168,6 +164,43 @@
text-decoration: revert;
}
+
+/*
+ * Attribution.
+ */
+
+.leaflet-container .leaflet-control-attribution {
+ background-color: #fff;
+ border-radius: 1.5em;
+ color: currentColor;
+ margin: 5px;
+ min-height: 30px;
+ min-width: 30px;
+ padding: 0;
+}
+
+.leaflet-control-attribution summary {
+ display: block;
+ list-style: none;
+ border-radius: 100%;
+ position: absolute;
+ bottom: 0;
+ left: calc(100% - 30px);
+ line-height: 0;
+ width: 30px;
+ height: 30px;
+}
+
+.leaflet-control-attribution summary svg {
+ border-radius: 100%;
+ width: inherit;
+ height: inherit;
+}
+
+.mapml-attribution-container {
+ padding: 5px 35px 5px 10px;
+}
+
/*
* Zoom controls.
*/
diff --git a/src/mapml/control/AttributionButton.js b/src/mapml/control/AttributionButton.js
new file mode 100644
index 000000000..baf0aadc2
--- /dev/null
+++ b/src/mapml/control/AttributionButton.js
@@ -0,0 +1,73 @@
+export var AttributionButton = L.Control.Attribution.extend({
+ options: {
+ prefix: 'Maps for HTML Community Group | Leaflet '
+ },
+
+ onAdd: function (map) {
+ map.attributionControl = this;
+ this._container = L.DomUtil.create('details', 'leaflet-control-attribution');
+ L.DomEvent.disableClickPropagation(this._container);
+
+ for (var i in map._layers) {
+ if (map._layers[i].getAttribution) {
+ this.addAttribution(map._layers[i].getAttribution());
+ }
+ }
+
+ this._update();
+
+ map.on('layeradd', this._addAttribution, this);
+
+ let dialog = document.createElement("dialog");
+ dialog.setAttribute("class", "shortcuts-dialog");
+ dialog.setAttribute("autofocus", "");
+ dialog.onclick = function (e) {
+ e.stopPropagation();
+ };
+ dialog.innerHTML = `${M.options.locale.kbdShortcuts} ` +
+ `
${M.options.locale.kbdMovement}- ↑ ${M.options.locale.kbdPanUp}
- ↓ ${M.options.locale.kbdPanDown}
- ← ${M.options.locale.kbdPanLeft}
- → ${M.options.locale.kbdPanRight}
- + ${M.options.locale.btnZoomIn}
- - ${M.options.locale.btnZoomOut}
- shift + ←/↑/→/↓ 3x ${M.options.locale.kbdPanIncrement}
- ctrl + ←/↑/→/↓ 0.2x ${M.options.locale.kbdPanIncrement}
- shift + +/- ${M.options.locale.kbdZoom}
` +
+ `${M.options.locale.kbdFeature}- ←/↑ ${M.options.locale.kbdPrevFeature}
- →/↓ ${M.options.locale.kbdNextFeature}
`;
+ map._container.appendChild(dialog);
+
+ return this._container;
+ },
+
+ _update: function () {
+ if (!this._map) { return; }
+
+ var attribs = [];
+
+ for (var i in this._attributions) {
+ if (this._attributions[i]) {
+ attribs.push(i);
+ }
+ }
+
+ var prefixAndAttribs = [];
+
+ if (this.options.prefix) {
+ prefixAndAttribs.push(this.options.prefix);
+ }
+ if (attribs.length) {
+ prefixAndAttribs.push(attribs.join(', '));
+ }
+ this._container.innerHTML = `` + '' + ` | ` + prefixAndAttribs.join(' | ') + '
';
+ this._container.setAttribute("role","group");
+ this._container.setAttribute("aria-label",`${M.options.locale.btnAttribution}`);
+ }
+});
+
+L.Map.mergeOptions({
+ attributionControl: false,
+ toggleableAttributionControl: true
+});
+
+L.Map.addInitHook(function () {
+ if (this.options.toggleableAttributionControl) {
+ new AttributionButton().addTo(this);
+ }
+});
+
+export var attributionButton = function (options) {
+ return new AttributionButton(options);
+};
diff --git a/src/mapml/control/AttributionControl.js b/src/mapml/control/AttributionControl.js
deleted file mode 100644
index f7b3d8171..000000000
--- a/src/mapml/control/AttributionControl.js
+++ /dev/null
@@ -1,17 +0,0 @@
-export var attributionControl = function (map) {
- map._attributionControl = map._map.attributionControl.setPrefix(` | Maps4HTML | Leaflet`);
-
- let dialog = document.createElement("dialog");
- dialog.setAttribute("class", "shortcuts-dialog");
- dialog.setAttribute("autofocus", "");
- dialog.onclick = function (e) {
- e.stopPropagation();
- };
- dialog.innerHTML = `${M.options.locale.kbdShortcuts} ` +
- `${M.options.locale.kbdMovement}- ↑ ${M.options.locale.kbdPanUp}
- ↓ ${M.options.locale.kbdPanDown}
- ← ${M.options.locale.kbdPanLeft}
- → ${M.options.locale.kbdPanRight}
- + ${M.options.locale.btnZoomIn}
- - ${M.options.locale.btnZoomOut}
- shift + ←/↑/→/↓ 3x ${M.options.locale.kbdPanIncrement}
- ctrl + ←/↑/→/↓ 0.2x ${M.options.locale.kbdPanIncrement}
- shift + +/- ${M.options.locale.kbdZoom}
` +
- `${M.options.locale.kbdFeature}- ←/↑ ${M.options.locale.kbdPrevFeature}
- →/↓ ${M.options.locale.kbdNextFeature}
`;
- map._container.appendChild(dialog);
-
- map._attributionControl.getContainer().setAttribute("role","group");
- map._attributionControl.getContainer().setAttribute("aria-label","Map data attribution");
-};
\ No newline at end of file
diff --git a/src/mapml/index.js b/src/mapml/index.js
index b1a9bb338..7a7cdd0d5 100644
--- a/src/mapml/index.js
+++ b/src/mapml/index.js
@@ -51,20 +51,20 @@ import { DebugOverlay, debugOverlay} from './layers/DebugOverlay';
import { QueryHandler } from './handlers/QueryHandler';
import { ContextMenu } from './handlers/ContextMenu';
import { Util } from './utils/Util';
+import { AttributionButton, attributionButton } from './control/AttributionButton';
import { ReloadButton, reloadButton } from './control/ReloadButton';
import { ScaleBar, scaleBar } from './control/ScaleBar';
import { FullscreenButton, fullscreenButton } from './control/FullscreenButton';
-import { attributionControl } from "./control/AttributionControl";
-import {geolocationButton} from "./control/GeolocationButton";
+import { geolocationButton } from "./control/GeolocationButton";
import { Crosshair, crosshair } from "./layers/Crosshair";
import { Feature, feature } from "./features/feature";
import { FeatureRenderer, featureRenderer } from './features/featureRenderer';
import { FeatureGroup, featureGroup} from './features/featureGroup';
-import {AnnounceMovement} from "./handlers/AnnounceMovement";
+import { AnnounceMovement } from "./handlers/AnnounceMovement";
import { FeatureIndex } from "./handlers/FeatureIndex";
import { Options } from "./options";
import "./keyboard";
-import {featureIndexOverlay, FeatureIndexOverlay} from "./layers/FeatureIndexOverlay";
+import { featureIndexOverlay, FeatureIndexOverlay } from "./layers/FeatureIndexOverlay";
/* global L, Node */
(function (window, document, undefined) {
@@ -653,6 +653,9 @@ M.featureLayer = featureLayer;
M.LayerControl = LayerControl;
M.layerControl = layerControl;
+M.AttributionButton = AttributionButton;
+M.attributionButton = attributionButton;
+
M.ReloadButton = ReloadButton;
M.reloadButton = reloadButton;
@@ -662,8 +665,6 @@ M.scaleBar = scaleBar;
M.FullscreenButton = FullscreenButton;
M.fullscreenButton = fullscreenButton;
-M.attributionControl = attributionControl;
-
M.geolocationButton = geolocationButton;
M.StaticTileLayer = StaticTileLayer;
diff --git a/src/mapml/options.js b/src/mapml/options.js
index bc5d237ac..fbf26875c 100644
--- a/src/mapml/options.js
+++ b/src/mapml/options.js
@@ -19,6 +19,7 @@ export var Options = {
lmZoomToLayer: "Zoom To Layer",
lmCopyLayer: "Copy Layer",
lcOpacity: "Opacity",
+ btnAttribution: "Map data attribution",
btnZoomIn: "Zoom in",
btnZoomOut: "Zoom out",
btnFullScreen: "View Fullscreen",
diff --git a/src/web-map.js b/src/web-map.js
index 41a0e8ae5..ad539e94c 100644
--- a/src/web-map.js
+++ b/src/web-map.js
@@ -275,8 +275,6 @@ export class WebMap extends HTMLMapElement {
fadeAnimation: true
});
this._addToHistory();
- // the attribution control is not optional
- M.attributionControl(this);
this._createControls();
this._toggleControls();
diff --git a/test/e2e/core/kbdAttribution.test.js b/test/e2e/core/kbdAttribution.test.js
index cc9ad205c..64c4de21a 100644
--- a/test/e2e/core/kbdAttribution.test.js
+++ b/test/e2e/core/kbdAttribution.test.js
@@ -18,6 +18,8 @@ test.describe("Keyboard shortcut attribution test", ()=> {
await page.keyboard.press("Tab");
}
+ await page.keyboard.press("Enter");
+ await page.keyboard.press("Tab");
await page.keyboard.press("Enter");
const dialog = await page.$eval("body > mapml-viewer div > dialog",
(dialog) => dialog.hasAttribute("open")
diff --git a/test/e2e/core/mapElement.test.js b/test/e2e/core/mapElement.test.js
index 7fdcda663..ba72da4e1 100644
--- a/test/e2e/core/mapElement.test.js
+++ b/test/e2e/core/mapElement.test.js
@@ -90,9 +90,9 @@ test.describe("Playwright Map Element Tests", () => {
await expect(projection).toEqual("OSMTILE");
});
test("Ensure attribution control has role='group' aria-label='Map data attribution'", async () => {
- let role = await page.evaluate(`document.querySelector('map')._attributionControl.getContainer().getAttribute('role')`);
+ let role = await page.evaluate(`document.querySelector('map')._map.attributionControl.getContainer().getAttribute('role')`);
await expect(role).toEqual("group");
- let arialabel = await page.evaluate(`document.querySelector('map')._attributionControl.getContainer().getAttribute('aria-label')`);
+ let arialabel = await page.evaluate(`document.querySelector('map')._map.attributionControl.getContainer().getAttribute('aria-label')`);
await expect(arialabel).toEqual("Map data attribution");
});
-});
\ No newline at end of file
+});
diff --git a/test/e2e/core/popupTabNavigation.test.js b/test/e2e/core/popupTabNavigation.test.js
index 4f0cfe0d1..bb1b1985f 100644
--- a/test/e2e/core/popupTabNavigation.test.js
+++ b/test/e2e/core/popupTabNavigation.test.js
@@ -191,6 +191,17 @@ test.describe("Playwright Keyboard Navigation + Query Layer Tests" , () => {
});
test("Focus Controls focuses the first