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 | Slava Ukraini 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} ` + + `` + + ``; + 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 | Slava Ukraini 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} ` + - `` + - ``; - 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