aboutsummaryrefslogtreecommitdiff
path: root/modern/src/map
diff options
context:
space:
mode:
authorAnton Tananaev <anton.tananaev@gmail.com>2020-11-01 15:37:23 -0800
committerAnton Tananaev <anton.tananaev@gmail.com>2020-11-01 15:37:23 -0800
commite0b977f7f50b4174382df7787f63d58dac3e6ed8 (patch)
tree8627f65501afac3f6518da29b9358b81f91f97fb /modern/src/map
parent76ef2b38fac5c58264921479b0e7d19115bb9c7d (diff)
downloadtrackermap-web-e0b977f7f50b4174382df7787f63d58dac3e6ed8.tar.gz
trackermap-web-e0b977f7f50b4174382df7787f63d58dac3e6ed8.tar.bz2
trackermap-web-e0b977f7f50b4174382df7787f63d58dac3e6ed8.zip
Implement map switching
Diffstat (limited to 'modern/src/map')
-rw-r--r--modern/src/map/Map.js52
-rw-r--r--modern/src/map/switcher/switcher.css40
-rw-r--r--modern/src/map/switcher/switcher.js79
3 files changed, 156 insertions, 15 deletions
diff --git a/modern/src/map/Map.js b/modern/src/map/Map.js
index 6cb3949b..2617b98d 100644
--- a/modern/src/map/Map.js
+++ b/modern/src/map/Map.js
@@ -1,9 +1,11 @@
import 'mapbox-gl/dist/mapbox-gl.css';
+import './switcher/switcher.css';
import mapboxgl from 'mapbox-gl';
+import { SwitcherControl } from './switcher/switcher';
import React, { useRef, useLayoutEffect, useEffect, useState } from 'react';
import { deviceCategories } from '../common/deviceCategories';
import { loadIcon, loadImage } from './mapUtil';
-import { styleOsm } from './mapStyles';
+import { styleCarto, styleOsm } from './mapStyles';
const element = document.createElement('div');
element.style.width = '100%';
@@ -14,17 +16,17 @@ export const map = new mapboxgl.Map({
style: styleOsm(),
});
-map.addControl(new mapboxgl.NavigationControl());
+let ready = false;
+const readyListeners = new Set();
-let readyListeners = [];
+const addReadyListener = listener => readyListeners.add(listener);
-const onMapReady = listener => {
- if (!readyListeners) {
- listener();
- } else {
- readyListeners.push(listener);
- }
-};
+const removeReadyListener = listener => readyListeners.delete(listener);
+
+const updateReadyValue = value => {
+ ready = value;
+ readyListeners.forEach(listener => listener(value));
+}
map.on('load', async () => {
const background = await loadImage('images/background.svg');
@@ -32,18 +34,38 @@ map.on('load', async () => {
const imageData = await loadIcon(category, background, `images/icon/${category}.svg`);
map.addImage(category, imageData, { pixelRatio: window.devicePixelRatio });
}));
- if (readyListeners) {
- readyListeners.forEach(listener => listener());
- readyListeners = null;
- }
+ updateReadyValue(true);
});
+map.addControl(new mapboxgl.NavigationControl({
+ showCompass: false,
+}));
+
+map.addControl(new SwitcherControl(
+ [{
+ title: "styleOsm",
+ uri: styleOsm(),
+ }, {
+ title: "styleCarto",
+ uri: styleCarto(),
+ }],
+ 'styleOsm',
+ () => updateReadyValue(false),
+ () => updateReadyValue(true),
+));
+
const Map = ({ children }) => {
const containerEl = useRef(null);
const [mapReady, setMapReady] = useState(false);
- useEffect(() => onMapReady(() => setMapReady(true)), []);
+ useEffect(() => {
+ const listener = ready => setMapReady(ready);
+ addReadyListener(listener);
+ return () => {
+ removeReadyListener(listener);
+ };
+ }, []);
useLayoutEffect(() => {
const currentEl = containerEl.current;
diff --git a/modern/src/map/switcher/switcher.css b/modern/src/map/switcher/switcher.css
new file mode 100644
index 00000000..6c8fdb4e
--- /dev/null
+++ b/modern/src/map/switcher/switcher.css
@@ -0,0 +1,40 @@
+.mapboxgl-style-list
+{
+ display: none;
+}
+
+.mapboxgl-ctrl-group .mapboxgl-style-list button
+{
+ background: none;
+ border: none;
+ cursor: pointer;
+ display: block;
+ font-size: 14px;
+ padding: 8px 8px 6px;
+ text-align: right;
+ width: 100%;
+ height: auto;
+}
+
+.mapboxgl-style-list button.active
+{
+ font-weight: bold;
+}
+
+.mapboxgl-style-list button:hover
+{
+ background-color: rgba(0, 0, 0, 0.05);
+}
+
+.mapboxgl-style-list button + button
+{
+ border-top: 1px solid #ddd;
+}
+
+.mapboxgl-style-switcher
+{
+ background-image: url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iaXNvLTg4NTktMSI/PjwhRE9DVFlQRSBzdmcgUFVCTElDICItLy9XM0MvL0RURCBTVkcgMS4xLy9FTiIgImh0dHA6Ly93d3cudzMub3JnL0dyYXBoaWNzL1NWRy8xLjEvRFREL3N2ZzExLmR0ZCI+PHN2ZyB2ZXJzaW9uPSIxLjEiIGlkPSJDYXBhXzEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHg9IjBweCIgeT0iMHB4IiB3aWR0aD0iNTQuODQ5cHgiIGhlaWdodD0iNTQuODQ5cHgiIHZpZXdCb3g9IjAgMCA1NC44NDkgNTQuODQ5IiBzdHlsZT0iZW5hYmxlLWJhY2tncm91bmQ6bmV3IDAgMCA1NC44NDkgNTQuODQ5OyIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+PGc+PGc+PGc+PHBhdGggZD0iTTU0LjQ5NywzOS42MTRsLTEwLjM2My00LjQ5bC0xNC45MTcsNS45NjhjLTAuNTM3LDAuMjE0LTEuMTY1LDAuMzE5LTEuNzkzLDAuMzE5Yy0wLjYyNywwLTEuMjU0LTAuMTA0LTEuNzktMC4zMThsLTE0LjkyMS01Ljk2OEwwLjM1MSwzOS42MTRjLTAuNDcyLDAuMjAzLTAuNDY3LDAuNTI0LDAuMDEsMC43MTZMMjYuNTYsNTAuODFjMC40NzcsMC4xOTEsMS4yNTEsMC4xOTEsMS43MjksMEw1NC40ODgsNDAuMzNDNTQuOTY0LDQwLjEzOSw1NC45NjksMzkuODE3LDU0LjQ5NywzOS42MTR6Ii8+PHBhdGggZD0iTTU0LjQ5NywyNy41MTJsLTEwLjM2NC00LjQ5MWwtMTQuOTE2LDUuOTY2Yy0wLjUzNiwwLjIxNS0xLjE2NSwwLjMyMS0xLjc5MiwwLjMyMWMtMC42MjgsMC0xLjI1Ni0wLjEwNi0xLjc5My0wLjMyMWwtMTQuOTE4LTUuOTY2TDAuMzUxLDI3LjUxMmMtMC40NzIsMC4yMDMtMC40NjcsMC41MjMsMC4wMSwwLjcxNkwyNi41NiwzOC43MDZjMC40NzcsMC4xOSwxLjI1MSwwLjE5LDEuNzI5LDBsMjYuMTk5LTEwLjQ3OUM1NC45NjQsMjguMDM2LDU0Ljk2OSwyNy43MTYsNTQuNDk3LDI3LjUxMnoiLz48cGF0aCBkPSJNMC4zNjEsMTYuMTI1bDEzLjY2Miw1LjQ2NWwxMi41MzcsNS4wMTVjMC40NzcsMC4xOTEsMS4yNTEsMC4xOTEsMS43MjksMGwxMi41NDEtNS4wMTZsMTMuNjU4LTUuNDYzYzAuNDc3LTAuMTkxLDAuNDgtMC41MTEsMC4wMS0wLjcxNkwyOC4yNzcsNC4wNDhjLTAuNDcxLTAuMjA0LTEuMjM2LTAuMjA0LTEuNzA4LDBMMC4zNTEsMTUuNDFDLTAuMTIxLDE1LjYxNC0wLjExNiwxNS45MzUsMC4zNjEsMTYuMTI1eiIvPjwvZz48L2c+PC9nPjxnPjwvZz48Zz48L2c+PGc+PC9nPjxnPjwvZz48Zz48L2c+PGc+PC9nPjxnPjwvZz48Zz48L2c+PGc+PC9nPjxnPjwvZz48Zz48L2c+PGc+PC9nPjxnPjwvZz48Zz48L2c+PGc+PC9nPjwvc3ZnPg==);
+ background-position: center;
+ background-repeat: no-repeat;
+ background-size: 70%;
+}
diff --git a/modern/src/map/switcher/switcher.js b/modern/src/map/switcher/switcher.js
new file mode 100644
index 00000000..ff9fbe97
--- /dev/null
+++ b/modern/src/map/switcher/switcher.js
@@ -0,0 +1,79 @@
+export class SwitcherControl {
+
+ constructor(styles, defaultStyle, beforeSwitch, afterSwitch) {
+ this.styles = styles;
+ this.defaultStyle = defaultStyle;
+ this.beforeSwitch = beforeSwitch;
+ this.afterSwitch = afterSwitch;
+ this.onDocumentClick = this.onDocumentClick.bind(this);
+ }
+
+ getDefaultPosition() {
+ return 'top-right';
+ }
+
+ onAdd(map) {
+ this.map = map;
+ this.controlContainer = document.createElement('div');
+ this.controlContainer.classList.add('mapboxgl-ctrl');
+ this.controlContainer.classList.add('mapboxgl-ctrl-group');
+ this.mapStyleContainer = document.createElement('div');
+ this.styleButton = document.createElement('button');
+ this.styleButton.type = 'button';
+ this.mapStyleContainer.classList.add('mapboxgl-style-list');
+ for (const style of this.styles) {
+ const styleElement = document.createElement('button');
+ styleElement.type = 'button';
+ styleElement.innerText = style.title;
+ styleElement.classList.add(style.title.replace(/[^a-z0-9-]/gi, '_'));
+ styleElement.dataset.uri = JSON.stringify(style.uri);
+ styleElement.addEventListener('click', event => {
+ const srcElement = event.srcElement;
+ if (srcElement.classList.contains('active')) {
+ return;
+ }
+ this.beforeSwitch();
+ this.map.setStyle(JSON.parse(srcElement.dataset.uri));
+ this.afterSwitch();
+ this.mapStyleContainer.style.display = 'none';
+ this.styleButton.style.display = 'block';
+ const elms = this.mapStyleContainer.getElementsByClassName('active');
+ while (elms[0]) {
+ elms[0].classList.remove('active');
+ }
+ srcElement.classList.add('active');
+ });
+ if (style.title === this.defaultStyle) {
+ styleElement.classList.add('active');
+ }
+ this.mapStyleContainer.appendChild(styleElement);
+ }
+ this.styleButton.classList.add('mapboxgl-ctrl-icon');
+ this.styleButton.classList.add('mapboxgl-style-switcher');
+ this.styleButton.addEventListener('click', () => {
+ this.styleButton.style.display = 'none';
+ this.mapStyleContainer.style.display = 'block';
+ });
+ document.addEventListener('click', this.onDocumentClick);
+ this.controlContainer.appendChild(this.styleButton);
+ this.controlContainer.appendChild(this.mapStyleContainer);
+ return this.controlContainer;
+ }
+
+ onRemove() {
+ if (!this.controlContainer || !this.controlContainer.parentNode || !this.map || !this.styleButton) {
+ return;
+ }
+ this.styleButton.removeEventListener('click', this.onDocumentClick);
+ this.controlContainer.parentNode.removeChild(this.controlContainer);
+ document.removeEventListener('click', this.onDocumentClick);
+ this.map = undefined;
+ }
+
+ onDocumentClick(event) {
+ if (this.controlContainer && !this.controlContainer.contains(event.target) && this.mapStyleContainer && this.styleButton) {
+ this.mapStyleContainer.style.display = 'none';
+ this.styleButton.style.display = 'block';
+ }
+ }
+}