leaflet#control TypeScript Examples

The following examples show how to use leaflet#control. You can vote up the ones you like or vote down the ones you don't like, and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: LinkControl.ts    From LiveAtlas with Apache License 2.0 6 votes vote down vote up
/**
 * Leaflet map control providing a button for copying a link for the current map view to the clipboard
 */
export class LinkControl extends Control {
	declare options: ControlOptions

	constructor(options: ControlOptions) {
		super(options);
	}

	onAdd() {
		const store = useStore(),
			linkButton = DomUtil.create('button',
				'leaflet-control-button leaflet-control-link') as HTMLButtonElement,
			copySuccessMessage = computed(() => store.state.messages.copyToClipboardSuccess),
			copyErrorMessage = computed(() => store.state.messages.copyToClipboardError);

		linkButton.type = 'button';
		linkButton.title = store.state.messages.linkTitle;
		linkButton.innerHTML = `
		<svg class="svg-icon" aria-hidden="true">
		  <use xlink:href="#icon--link" />
		</svg>`;

		linkButton.addEventListener('click', e => {
			e.preventDefault();
			toClipboard(window.location.href.split("#")[0] + store.getters.url).then(() => {
				notify(copySuccessMessage.value);
			}).catch((e) => {
				notify({ type: 'error', text: copyErrorMessage.value });
				console.error('Error copying to clipboard', e);
			});

		});

		return linkButton;
	}
}
Example #2
Source File: LogoControl.ts    From LiveAtlas with Apache License 2.0 6 votes vote down vote up
/**
 * Leaflet map control which displays an arbitrary image or text with an optional link
 * Intended for use for dynmap logo components
 */
export class LogoControl extends Control {
	declare options: LogoControlOptions;

	constructor(options: LogoControlOptions) {
		super(options);
	}

	onAdd() {
		const container = DomUtil.create('div', 'leaflet-control-logo');
		let link;

		if (this.options.url) {
			link = DomUtil.create('a', '', container) as HTMLAnchorElement;
			link.href = this.options.url;
			link.setAttribute('aria-label', this.options.text);
		}

		if (this.options.image) {
			const image = DomUtil.create('img', '', link) as HTMLImageElement;
			image.src = this.options.image;
			image.alt = this.options.text;
		} else {
			container.textContent = this.options.text;
		}

		return container;
	}
}
Example #3
Source File: map.component.ts    From dayz-server-manager with MIT License 6 votes vote down vote up
private updateLayersControl(): void {
        if (this.layerControl) {
            this.layerControl.remove();
        }

        const overlays = {} as any;

        for (const x of this.layers) {
            if (x[1].layer) {
                overlays[x[1].label] = x[1].layer;
            }
        }

        this.layerControl = control.layers(
            this.baseLayers,
            overlays,
        );
        this.map?.addControl(this.layerControl);
    }
Example #4
Source File: ChatControl.ts    From LiveAtlas with Apache License 2.0 5 votes vote down vote up
/**
 * Leaflet map control providing a chat button which opens the chatbox on click
 */
export class ChatControl extends Control {
	declare options: ControlOptions

	constructor(options: ControlOptions) {
		super(options);
	}

	onAdd() {
		const store = useStore(),
			chatButton = DomUtil.create('button',
				'leaflet-control-bottom leaflet-control-button leaflet-control-chat') as HTMLButtonElement;

		chatButton.type = 'button';
		chatButton.title = store.state.messages.chatTitle;
		chatButton.innerHTML = `
		<svg class="svg-icon">
		  <use xlink:href="#icon--chat" />
		</svg>`;

		chatButton.addEventListener('click', e => {
			store.commit(MutationTypes.TOGGLE_UI_ELEMENT_VISIBILITY, 'chat');
			e.stopPropagation();
			e.preventDefault();
		});

		//Open chat on ArrowRight from button
		DomEvent.on(chatButton,'keydown', (e: Event) => {
			if((e as KeyboardEvent).key === 'ArrowRight') {
				store.commit(MutationTypes.SET_UI_ELEMENT_VISIBILITY, {element: 'chat', state: true});
			}
		});

		watch(store.state.ui.visibleElements, (newValue) => {
			chatButton.setAttribute('aria-expanded', newValue.has('chat').toString());
		});

		return chatButton;
	}
}
Example #5
Source File: map.component.ts    From dayz-server-manager with MIT License 5 votes vote down vote up
public baseLayers?: Control.LayersObject;
Example #6
Source File: map.component.ts    From dayz-server-manager with MIT License 5 votes vote down vote up
private layerControl?: Control;
Example #7
Source File: ClockControl.ts    From LiveAtlas with Apache License 2.0 4 votes vote down vote up
/**
 * Leaflet map control providing a clock which can display the current in-game time of day and weather
 */
export class ClockControl extends Control {
	declare options: ClockControlOptions;
	declare _container?: HTMLElement;

	private _sun?: HTMLElement;
	private _moon?: HTMLElement;
	private _clock?: HTMLElement;
	private _currentMoonIcon?: string;
	private _currentSunIcon?: string;
	private _unwatchHandler?: Function;

	constructor(options: ClockControlOptions) {
		super(Object.assign(options, {position: 'topcenter'}));

		Util.setOptions(this, options);
	}

	onAdd() {
		const digital = !this.options.showTimeOfDay && !this.options.showWeather && this.options.showDigitalClock,
			worldState = useStore().state.currentWorldState;

		this._container = DomUtil.create('div', 'clock' + (digital ? ' clock--digital' : ''));
		this._sun = DomUtil.create('div', 'clock__sun', this._container);
		this._moon = DomUtil.create('div', 'clock__moon', this._container);

		this._sun.style.transform = 'translate(-150px, -150px)';
		this._moon.style.transform = 'translate(-150px, -150px)';

		this._sun!.innerHTML = `
		<svg class="svg-icon" aria-hidden="true">
	  		<use xlink:href="#icon--clock_sun" />
		</svg>`;
		this._moon!.innerHTML = `
		<svg class="svg-icon" aria-hidden="true">
	  		<use xlink:href="#icon--clock_moon" />
		</svg>`;

		if (this.options.showDigitalClock) {
			this._clock = DomUtil.create('div', 'clock__time', this._container)
		}

		this._unwatchHandler = watch(worldState, (newValue) => {
			this._update(newValue);
		}, { deep: true });

		return this._container;
	}

	onRemove() {
		if(this._unwatchHandler) {
			this._unwatchHandler();
		}
	}

	_update(worldState: LiveAtlasWorldState) {
		const timeOfDay = worldState.timeOfDay;
		let sunAngle;

		if (timeOfDay > 23100 || timeOfDay < 12900) {
			//day mode
			let movedTime = timeOfDay + 900;
			movedTime = (movedTime >= 24000) ? movedTime - 24000 : movedTime;
			//Now we have 0 -> 13800 for the day period
			//Divide by 13800*2=27600 instead of 24000 to compress day
			sunAngle = ((movedTime) / 27600 * 2 * Math.PI);
		} else {
			//night mode
			const movedTime = timeOfDay - 12900;
			//Now we have 0 -> 10200 for the night period
			//Divide by 10200*2=20400 instead of 24000 to expand night
			sunAngle = Math.PI + ((movedTime) / 20400 * 2 * Math.PI);
		}

		const moonAngle = sunAngle + Math.PI;

		if (timeOfDay >= 0) {
			this._sun!.style.transform = 'translate(' + Math.round(-50 * Math.cos(sunAngle)) + 'px, ' + Math.round(-50 * Math.sin(sunAngle)) + 'px)';
			this._moon!.style.transform = 'translate(' + Math.round(-50 * Math.cos(moonAngle)) + 'px, ' + Math.round(-50 * Math.sin(moonAngle)) + 'px)';
		} else {
			this._sun!.style.transform = 'translate(-150px, -150px)';
			this._moon!.style.transform = 'translate(-150px, -150px)';
		}

		const minecraftTime = getMinecraftTime(timeOfDay);

		if (this.options.showDigitalClock) {
			if (timeOfDay >= 0) {
				this._clock!.classList.remove(minecraftTime.night ? 'day' : 'night');
				this._clock!.classList.add(minecraftTime.day ? 'day' : 'night');
				this._clock!.textContent = [
					minecraftTime.hours.toString().padStart(2, '0'),
					minecraftTime.minutes.toString().padStart(2, '0')
				].join(':');
			} else {
				this._clock!.classList.remove(minecraftTime.night ? 'day' : 'night');
				this._clock!.textContent = '';
			}
		}

		if (this.options.showWeather) {
			if (worldState.thundering) {
				this._setSunIcon('clock_sun_storm');
				this._setMoonIcon('clock_moon_storm');
			} else if (worldState.raining) {
				this._setSunIcon('clock_sun_rain');
				this._setMoonIcon('clock_moon_rain');
			} else {
				this._setSunIcon('clock_sun');
				this._setMoonIcon('clock_moon');
			}
		}
	}

	_setSunIcon(icon: string) {
		if(this._sun && this._currentSunIcon !== icon) {
			this._sun!.innerHTML = `
				<svg class="svg-icon" aria-hidden="true">
					<use xlink:href="#icon--${icon}" />
				</svg>`;
			this._currentSunIcon = icon;
		}
	}

	_setMoonIcon(icon: string) {
		if(this._moon && this._currentMoonIcon !== icon) {
			this._moon!.innerHTML = `
				<svg class="svg-icon" aria-hidden="true">
					<use xlink:href="#icon--${icon}" />
				</svg>`;
			this._currentMoonIcon = icon;
		}
	}
}
Example #8
Source File: CoordinatesControl.ts    From LiveAtlas with Apache License 2.0 4 votes vote down vote up
/**
 * Leaflet map control which displays in-game block coordinates when hovering over or tapping the map
 */
export class CoordinatesControl extends Control {
	declare options: CoordinatesControlOptions;
	declare _map ?: Map;

	private _location?: Coordinate;
	private _locationChanged: boolean = false;
	private readonly _coordsContainer: HTMLSpanElement;
	private readonly _regionContainer: HTMLSpanElement;
	private readonly _chunkContainer: HTMLSpanElement;

	constructor(options: CoordinatesControlOptions) {
		super(options);

		this._coordsContainer = DomUtil.create('span', 'value coordinates');
		this._chunkContainer = DomUtil.create('span', 'value chunk');
		this._regionContainer = DomUtil.create('span', 'value region');

		options.position = 'bottomleft';
		Util.setOptions(this, options);
	}

	onAdd(map: Map) {
		const container = DomUtil.create('div', 'leaflet-control-coordinates');

		this._coordsContainer.textContent = this.options.showY ? '-----, ---, -----' : '-----, -----';
		this._coordsContainer.dataset.label = this.options.label;
		container.appendChild(this._coordsContainer);

		if (this.options.showRegion) {
			this._regionContainer.textContent = '--------------';
			this._regionContainer.dataset.label = store.state.messages.locationRegion;
			container.appendChild(this._regionContainer);
		}

		if (this.options.showChunk) {
			this._chunkContainer.textContent = '----, ----';
			this._chunkContainer.dataset.label = store.state.messages.locationChunk;
			container.appendChild(this._chunkContainer);
		}

		map.on('mousemove', this._onMouseMove, this);
		map.on('mouseout', this._onMouseOut, this);

		return container;
	}

	remove() {
		if (!this._map) {
			return this;
		}

		this._map.off('mousemove', this._onMouseMove, this);
		this._map.off('mouseout', this._onMouseOut, this);
		super.remove();

		return this;
	}

	_onMouseMove(event: LeafletMouseEvent) {
		if (!this._map || !store.state.currentMap) {
			return;
		}

		this._location = store.state.currentMap.latLngToLocation(event.latlng, store.state.currentWorld!.seaLevel + 1);

		if(!this._locationChanged) {
			this._locationChanged = true;
			requestAnimationFrame(() => this._update());
		}
	}

	_onMouseOut() {
		if (!this._map) {
			return;
		}

		this._location = undefined;

		if(!this._locationChanged) {
			this._locationChanged = true;
			requestAnimationFrame(() => this._update());
		}
	}

	_update() {
		if (!this._map || !store.state.currentWorld || !this._locationChanged) {
			return;
		}

		this._locationChanged = false;

		if(!this._location) {
			if (this.options.showY) {
				this._coordsContainer.textContent = '-----, ---, -----';
			} else {
				this._coordsContainer.textContent = '-----, -----';
			}

			if (this.options.showRegion) {
				this._regionContainer.textContent = '--------------';
			}

			if (this.options.showChunk) {
				this._chunkContainer.textContent = '----, ----';
			}

			return;
		}

		const x = Math.round(this._location.x).toString().padStart(5, ' '),
			y = this._location.y.toString().padStart(3, ' '),
			z = Math.round(this._location.z).toString().padStart(5, ' '),
			regionX = Math.floor(this._location.x / 512).toString().padStart(3, ' '),
			regionZ = Math.floor(this._location.z / 512).toString().padStart(3, ' '),
			chunkX = Math.floor(this._location.x / 16).toString().padStart(4, ' '),
			chunkZ = Math.floor(this._location.z / 16).toString().padStart(4, ' ');

		if (this.options.showY) {
			this._coordsContainer.textContent = `${x}, ${y}, ${z}`;
		} else {
			this._coordsContainer.textContent = `${x}, ${z}`;
		}

		if (this.options.showRegion) {
			this._regionContainer.textContent = `r.${regionX}, ${regionZ}.mca`;
		}

		if (this.options.showChunk) {
			this._chunkContainer.textContent = `${chunkX}, ${chunkZ}`;
		}
	}
}
Example #9
Source File: LiveAtlasLayerControl.ts    From LiveAtlas with Apache License 2.0 4 votes vote down vote up
/**
 * Extension of leaflet's standard {@link Control.Layers}
 * Sorts layers by position, adds additional keyboard navigation, adjusts to viewport size and tracks expanded state in vuex
 */
export class LiveAtlasLayerControl extends Control.Layers {
	declare _map ?: LeafletMap;
	declare _overlaysList?: HTMLElement;
	declare _baseLayersList?: HTMLElement;
	declare _layerControlInputs?: HTMLElement[];
	declare _container?: HTMLElement;
	declare _section?: HTMLElement;
	declare _separator?: HTMLElement;

	private _layersButton?: HTMLElement;
	private _layerPositions: Map<Layer, number>;
	private visible: boolean = false;

	constructor(baseLayers?: LayersObject, overlays?: LayersObject, options?: LayersOptions) {
		// noinspection JSUnusedGlobalSymbols
		super(baseLayers, overlays, Object.assign(options, {
			sortLayers: true,
			sortFunction: (layer1: Layer, layer2: Layer, name1: string, name2: string) => {
				const priority1 = this._layerPositions.get(layer1) || 0,
					priority2 = this._layerPositions.get(layer2) || 0;

				if(priority1 !== priority2) {
					return priority1 - priority2;
				}

				return ((name1 < name2) ? -1 : ((name1 > name2) ? 1 : 0));
			}
		}));
		this._layerPositions = new Map<Layer, number>();
	}

	hasLayer(layer: Layer): boolean {
		// @ts-ignore
		return !!super._getLayer(Util.stamp(layer));
	}

	expand() {
		this._layersButton!.setAttribute('aria-expanded', 'true');
		this._section!.style.display = '';
		this.handleResize();

		const firstCheckbox = this._container!.querySelector('input');

		if(firstCheckbox) {
			(firstCheckbox as HTMLElement).focus();
		}

		// @ts-ignore
		super._checkDisabledLayers();
		return this;
	}

	collapse() {
		this._layersButton!.setAttribute('aria-expanded', 'false');
		this._section!.style.display = 'none';

		return this;
	}

	_initLayout() {
		const className = 'leaflet-control-layers',
			container = this._container = DomUtil.create('div', className),
			section = this._section = DomUtil.create('section', className + '-list'),
			button = this._layersButton = DomUtil.create('button', className + '-toggle', container);

		DomEvent.disableClickPropagation(container);
		DomEvent.disableScrollPropagation(container);

		//Open layer list on ArrowRight from button
		DomEvent.on(button,'keydown', (e: Event) => {
			if((e as KeyboardEvent).key === 'ArrowRight') {
				store.commit(MutationTypes.SET_UI_ELEMENT_VISIBILITY, {element: 'layers', state: true});
			}
		});

		DomEvent.on(container, 'keydown', (e: Event) => {
			//Close layer list on ArrowLeft from within list
			if((e as KeyboardEvent).key === 'ArrowLeft') {
				e.preventDefault();
				store.commit(MutationTypes.SET_UI_ELEMENT_VISIBILITY, {element: 'layers', state: false});
				nextTick(() => button.focus());
			}

			const elements = Array.from(container.querySelectorAll('input')) as HTMLElement[];
			handleKeyboardEvent(e as KeyboardEvent, elements);
		});
		DomEvent.on(button,'click', () => store.commit(MutationTypes.TOGGLE_UI_ELEMENT_VISIBILITY, 'layers'));

		section.style.display = 'none';

		button.title = store.state.messages.layersTitle;
		button.setAttribute('aria-expanded', 'false');
		button.innerHTML = `
			<svg class="svg-icon" aria-hidden="true">
			  <use xlink:href="#icon--layers" />
			</svg>`;


		//Use vuex track expanded state
		watch(store.state.ui.visibleElements, (newValue) => {
			if(newValue.has('layers') && !this.visible) {
				this.expand();
			} else if(this.visible && !newValue.has('layers')) {
				this.collapse();
			}

			this.visible = store.state.ui.visibleElements.has('layers');
		});

		watch(store.state.messages, (newValue) => (button.title = newValue.layersTitle));//

		this.visible = store.state.ui.visibleElements.has('layers');

		if (this.visible) {
			this.expand();
		}

		this._baseLayersList = DomUtil.create('div', className + '-base', section);
		this._separator = DomUtil.create('div', className + '-separator', section);
		this._overlaysList = DomUtil.create('div', className + '-overlays', section);

		container.appendChild(section);

		window.addEventListener('resize', () => this.handleResize());
		this.handleResize();
	}

	handleResize() {
		const y = this._layersButton!.getBoundingClientRect().y;

		//Limit height to remaining vertical space
		// Including 30px element padding, 10px padding from edge of viewport, and 55px padding to avoid covering bottom bar
		this._section!.style.maxHeight = `calc(100vh - ${(y + 30 + 10 + 55)}px)`;
	}

	addOverlayAtPosition(layer: Layer, name: string, position: number): this {
		this._layerPositions.set(layer, position);
		return super.addOverlay(layer, name);
	}

	addOverlay(layer: Layer, name: string): this {
		this._layerPositions.set(layer, 0);
		return super.addOverlay(layer, name);
	}

	removeLayer(layer: Layer): this {
		this._layerPositions.delete(layer);
		return super.removeLayer(layer);
	}

	// noinspection JSUnusedGlobalSymbols
	_addItem(obj: any) {
		const container = obj.overlay ? this._overlaysList : this._baseLayersList,
			item = document.createElement('label'),
			label = document.createElement('span'),
			checked = this._map!.hasLayer(obj.layer);

		let input;

		item.className = 'layer checkbox';

		if (obj.overlay) {
			input = document.createElement('input');
			input.type = 'checkbox';
			input.className = 'leaflet-control-layers-selector';
			input.defaultChecked = checked;
		} else {
			// @ts-ignore
			input = super._createRadioElement('leaflet-base-layers_' + Util.stamp(this), checked);
		}

		input.layerId = Util.stamp(obj.layer);
		this._layerControlInputs!.push(input);
		label.textContent = obj.name;

		// @ts-ignore
		DomEvent.on(input, 'click', (e: LeafletEvent) => super._onInputClick(e), this);

		item.appendChild(input);
		item.insertAdjacentHTML('beforeend',  `
		<svg class="svg-icon" aria-hidden="true">
	  		<use xlink:href="#icon--checkbox" />
		</svg>`);
		item.appendChild(label);

		container!.appendChild(item);

		// @ts-ignore
		super._checkDisabledLayers();
		return label;
	}

	onRemove(map: LeafletMap) {
		this._layerControlInputs = [];

		(super.onRemove as Function)(map);
	}
}
Example #10
Source File: LoadingControl.ts    From LiveAtlas with Apache License 2.0 4 votes vote down vote up
/**
 * Leaflet map control which displays a loading spinner when any tiles are loading
 */
export class LoadingControl extends Control {
	declare options: LoadingControlOptions;

	private _dataLoaders: Set<number> = new Set();
	private readonly _loadingIndicator: HTMLDivElement;
	private _delayIndicatorTimeout: null | ReturnType<typeof setTimeout> = null;

	constructor(options: LoadingControlOptions) {
		super(options);

		this._loadingIndicator = DomUtil.create('div',
			'leaflet-control-button leaflet-control-loading') as HTMLDivElement;
	}

	onAdd(map: Map) {
		this._loadingIndicator.title = useStore().state.messages.loadingTitle;
		this._loadingIndicator.hidden = true;
		this._loadingIndicator.innerHTML = `
		<svg class="svg-icon">
		  <use xlink:href="#icon--loading" />
		</svg>`;

		this._addLayerListeners(map);
		this._addMapListeners(map);

		return this._loadingIndicator;
	}

	onRemove(map: Map) {
		this._removeLayerListeners(map);
		this._removeMapListeners(map);
	}

	addLoader(id: number) {
		this._dataLoaders.add(id);

		if (this.options.delayIndicator && !this._delayIndicatorTimeout) {
			// If we are delaying showing the indicator and we're not
			// already waiting for that delay, set up a timeout.
			this._delayIndicatorTimeout = setTimeout(() => {
				this.updateIndicator();
				this._delayIndicatorTimeout = null;
			}, this.options.delayIndicator);
		} else {
			// Otherwise show the indicator immediately
			this.updateIndicator();
		}
	}

	removeLoader(id: number) {
		this._dataLoaders.delete(id);
		this.updateIndicator();

		// If removing this loader means we're in no danger of loading,
		// clear the timeout. This prevents old delays from instantly
		// triggering the indicator.
		if (this.options.delayIndicator && this._delayIndicatorTimeout && !this.isLoading()) {
			clearTimeout(this._delayIndicatorTimeout);
			this._delayIndicatorTimeout = null;
		}
	}

	updateIndicator() {
		if (this.isLoading()) {
			this._showIndicator();
		}
		else {
			this._hideIndicator();
		}
	}

	isLoading() {
		return this._countLoaders() > 0;
	}

	_countLoaders() {
		return this._dataLoaders.size;
	}

	_showIndicator() {
		this._loadingIndicator.hidden = false;
	}

	_hideIndicator() {
		this._loadingIndicator.hidden = true;
	}

	_handleLoading(e: LeafletEvent) {
		this.addLoader(this.getEventId(e));
	}

	_handleBaseLayerChange (e: LeafletEvent) {
		// Check for a target 'layer' that contains multiple layers, such as
		// L.LayerGroup. This will happen if you have an L.LayerGroup in an
		// L.Control.Layers.
		if (e.layer && e.layer.eachLayer && typeof e.layer.eachLayer === 'function') {
			e.layer.eachLayer((layer: Layer) => {
				this._handleBaseLayerChange({ layer: layer } as LeafletEvent);
			});
		}
	}

	_handleLoad(e: LeafletEvent) {
		this.removeLoader(this.getEventId(e));
	}

	getEventId(e: any) {
		if (e.id) {
			return e.id;
		} else if (e.layer) {
			return e.layer._leaflet_id;
		}
		return e.target._leaflet_id;
	}

	_layerAdd(e: LeafletEvent) {
		if(!(e.layer instanceof TileLayer)) {
			return;
		}

		try {
			if(e.layer.isLoading()) {
				this.addLoader((e.layer as any)._leaflet_id);
			}

			e.layer.on('loading', this._handleLoading, this);
			e.layer.on('load', this._handleLoad, this);
		} catch (exception) {
			console.warn('L.Control.Loading: Tried and failed to add ' +
				' event handlers to layer', e.layer);
			console.warn('L.Control.Loading: Full details', exception);
		}
	}

	_layerRemove(e: LeafletEvent) {
		if(!(e.layer instanceof TileLayer)) {
			return;
		}

		try {
			e.layer.off('loading', this._handleLoading, this);
			e.layer.off('load', this._handleLoad, this);
		} catch (exception) {
			console.warn('L.Control.Loading: Tried and failed to remove ' +
				'event handlers from layer', e.layer);
			console.warn('L.Control.Loading: Full details', exception);
		}
	}

	_addLayerListeners(map: Map) {
		// Add listeners for begin and end of load to any layers already
		// on the map
		map.eachLayer((layer: Layer) => {
			if(!(layer instanceof TileLayer)) {
				return;
			}

			if(layer.isLoading()) {
				this.addLoader((layer as any)._leaflet_id);
			}

			layer.on('loading', this._handleLoading, this);
			layer.on('load', this._handleLoad, this);
		});

		// When a layer is added to the map, add listeners for begin and
		// end of load
		map.on('layeradd', this._layerAdd, this);
		map.on('layerremove', this._layerRemove, this);
	}

	_removeLayerListeners(map: Map) {
		// Remove listeners for begin and end of load from all layers
		map.eachLayer((layer: Layer) => {
			if(!(layer instanceof TileLayer)) {
				return;
			}

			this.removeLoader((layer as any)._leaflet_id);

			layer.off('loading', this._handleLoading, this);
			layer.off('load', this._handleLoad, this);
		});

		// Remove layeradd/layerremove listener from map
		map.off('layeradd', this._layerAdd, this);
		map.off('layerremove', this._layerRemove, this);
	}

	_addMapListeners(map: Map) {
		// Add listeners to the map for (custom) dataloading and dataload
		// events, eg, for AJAX calls that affect the map but will not be
		// reflected in the above layer events.
		map.on('baselayerchange', this._handleBaseLayerChange, this);
		map.on('dataloading', this._handleLoading, this);
		map.on('dataload', this._handleLoad, this);
		map.on('layerremove', this._handleLoad, this);
	}

	_removeMapListeners(map: Map) {
		map.off('baselayerchange', this._handleBaseLayerChange, this);
		map.off('dataloading', this._handleLoading, this);
		map.off('dataload', this._handleLoad, this);
		map.off('layerremove', this._handleLoad, this);
	}
}
Example #11
Source File: LoginControl.ts    From LiveAtlas with Apache License 2.0 4 votes vote down vote up
/**
 * Leaflet map control providing a login/logout button which opens the login modal/logs out on click
 */
export class LoginControl extends Control {
	declare _map: LiveAtlasLeafletMap;
	declare options: ControlOptions;

	private readonly store = useStore();
	private readonly loggedIn = computed(() => this.store.state.loggedIn);
	private readonly _button: HTMLButtonElement;

	constructor(options: ControlOptions) {
		super(options);

		this._button = DomUtil.create('button',
				'leaflet-control-bottom leaflet-control-button leaflet-control-login') as HTMLButtonElement;

		this._button.type = 'button';

		this._button.addEventListener('click', async e => {
			e.stopPropagation();
			e.preventDefault();

			await this.handleClick();
		});

		//Open login on ArrowRight from button
		DomEvent.on(this._button,'keydown', async (e: Event) => {
			if ((e as KeyboardEvent).key === 'ArrowRight') {
				e.stopPropagation();
				e.preventDefault();

				await this.handleClick();
			}
		});

		watch(this.loggedIn, () => {
			this.update();
		});

		const visibleModal = computed(() => this.store.state.ui.visibleModal);

		watch(visibleModal, (newValue, oldValue) => {
			this._button.setAttribute('aria-expanded', (newValue === 'login').toString());

			if(this._map && !newValue && oldValue === 'login') {
				this._button.focus();
			}
		});

		this.update();
	}

	onAdd() {
		return this._button;
	}

	private update() {
		this._button.title = this.loggedIn.value
			? this.store.state.messages.logoutTitle : this.store.state.messages.loginTitle;
		this._button.innerHTML = `
			<svg class="svg-icon">
			  <use xlink:href="#icon--${this.loggedIn.value ? 'logout' : 'login'}" />
			</svg>`;
	}

	private async handleClick() {
		const logoutSuccess = computed(() => this.store.state.messages.logoutSuccess),
			logoutError = computed(() => this.store.state.messages.logoutErrorUnknown);

		if (this.loggedIn.value) {
			try {
				await this.store.dispatch(ActionTypes.LOGOUT, undefined);
				notify(logoutSuccess.value);
			} catch(e) {
				notify(logoutError.value);
			}
		} else {
			this.store.commit(MutationTypes.SHOW_UI_MODAL, 'login');
		}
	}
}