leaflet
Version:
JavaScript library for mobile-friendly interactive maps
171 lines (137 loc) • 4.25 kB
JavaScript
import {Class} from '../core/Class.js';
import {Map} from '../map/Map.js';
import * as Util from '../core/Util.js';
import * as DomUtil from '../dom/DomUtil.js';
/*
* @class Control
* @inherits Class
*
* Control is a base class for implementing map controls. Handles positioning.
* All other controls extend from this class.
*/
export class Control extends Class {
static {
// @section
// @aka Control Options
this.setDefaultOptions({
// @option position: String = 'topright'
// The position of the control (one of the map corners). Possible values are `'topleft'`,
// `'topright'`, `'bottomleft'` or `'bottomright'`
position: 'topright'
});
}
initialize(options) {
Util.setOptions(this, options);
}
/* @section
* Classes extending Control will inherit the following methods:
*
* @method getPosition: string
* Returns the position of the control.
*/
getPosition() {
return this.options.position;
}
// @method setPosition(position: string): this
// Sets the position of the control.
setPosition(position) {
const map = this._map;
map?.removeControl(this);
this.options.position = position;
map?.addControl(this);
return this;
}
// @method getContainer: HTMLElement
// Returns the HTMLElement that contains the control.
getContainer() {
return this._container;
}
// @method addTo(map: Map): this
// Adds the control to the given map.
addTo(map) {
this.remove();
this._map = map;
const container = this._container = this.onAdd(map),
pos = this.getPosition(),
corner = map._controlCorners[pos];
container.classList.add('leaflet-control');
if (pos.includes('bottom')) {
corner.insertBefore(container, corner.firstChild);
} else {
corner.appendChild(container);
}
this._map.on('unload', this.remove, this);
return this;
}
// @method remove: this
// Removes the control from the map it is currently active on.
remove() {
if (!this._map) {
return this;
}
this._container.remove();
if (this.onRemove) {
this.onRemove(this._map);
}
this._map.off('unload', this.remove, this);
this._map = null;
return this;
}
_refocusOnMap(e) {
// We exclude keyboard-click event to keep the focus on the control for accessibility.
// The position of keyboard-click events are x=0 and y=0.
if (this._map && e && !(e.screenX === 0 && e.screenY === 0)) {
this._map.getContainer().focus();
}
}
}
/* @section Extension methods
* @uninheritable
*
* Every control should extend from `Control` and (re-)implement the following methods.
*
* @method onAdd(map: Map): HTMLElement
* Should return the container DOM element for the control and add listeners on relevant map events. Called on [`control.addTo(map)`](#control-addTo).
*
* @method onRemove(map: Map)
* Optional method. Should contain all clean up code that removes the listeners previously added in [`onAdd`](#control-onadd). Called on [`control.remove()`](#control-remove).
*/
/* @namespace Map
* @section Methods for Layers and Controls
*/
Map.include({
// @method addControl(control: Control): this
// Adds the given control to the map
addControl(control) {
control.addTo(this);
return this;
},
// @method removeControl(control: Control): this
// Removes the given control from the map
removeControl(control) {
control.remove();
return this;
},
_initControlPos() {
const corners = this._controlCorners = {},
l = 'leaflet-',
container = this._controlContainer =
DomUtil.create('div', `${l}control-container`, this._container);
function createCorner(vSide, hSide) {
const className = `${l + vSide} ${l}${hSide}`;
corners[vSide + hSide] = DomUtil.create('div', className, container);
}
createCorner('top', 'left');
createCorner('top', 'right');
createCorner('bottom', 'left');
createCorner('bottom', 'right');
},
_clearControlPos() {
for (const c of Object.values(this._controlCorners)) {
c.remove();
}
this._controlContainer.remove();
delete this._controlCorners;
delete this._controlContainer;
}
});