Files
directus/app/src/utils/geometry/controls.ts
Oreille 475f6349f0 Map layout and interface improvements (#8628)
* Map layout and interface improvements:
* Disable drag to rotate
* Add keyboard shortcut to delete items
* Hide unselect button when selection is empty
* Add display template setting
* Fixed fitData button behaviour

* Removed unused hoveredFeatureId

* Added translations

* Expose clearFilters to the layout.
2021-10-12 09:50:18 -04:00

194 lines
6.0 KiB
TypeScript

import { Map, Point } from 'maplibre-gl';
export class ButtonControl {
active: boolean;
element: HTMLElement;
groupElement?: HTMLElement;
constructor(private className: string, private callback: (...args: any) => any) {
this.element = document.createElement('button');
this.element.className = this.className;
this.element.onclick = callback;
this.active = false;
}
click(...args: any[]): void {
this.callback(...args);
}
activate(yes: boolean): void {
this.element.classList[yes ? 'add' : 'remove']('active');
this.active = yes;
}
show(yes: boolean): void {
this.element.classList[yes ? 'remove' : 'add']('hidden');
}
onAdd(): HTMLElement {
this.groupElement = document.createElement('div');
this.groupElement.className = 'mapboxgl-ctrl mapboxgl-ctrl-group';
this.groupElement.appendChild(this.element);
return this.groupElement;
}
onRemove(): void {
this.element.remove();
this.groupElement?.remove();
}
}
type BoxSelectControlOptions = {
groupElementClass?: string;
boxElementClass?: string;
selectButtonClass?: string;
unselectButtonClass?: string;
layers: string[];
};
export class BoxSelectControl {
groupElement: HTMLElement;
boxElement: HTMLElement;
selectButton: ButtonControl;
unselectButton: ButtonControl;
map?: Map & { fire: (event: string, data?: any) => void };
layers: string[];
selecting = false;
shiftPressed = false;
startPos: Point | undefined;
lastPos: Point | undefined;
onKeyDownHandler: (event: KeyboardEvent) => any;
onKeyUpHandler: (event: KeyboardEvent) => any;
onMouseDownHandler: (event: MouseEvent) => any;
onMouseMoveHandler: (event: MouseEvent) => any;
onMouseUpHandler: (event: MouseEvent) => any;
constructor(options: BoxSelectControlOptions) {
this.layers = options?.layers ?? [];
this.boxElement = document.createElement('div');
this.boxElement.className = options?.boxElementClass ?? 'selection-box';
this.groupElement = document.createElement('div');
this.groupElement.className = options?.groupElementClass ?? 'mapboxgl-ctrl mapboxgl-ctrl-group';
this.selectButton = new ButtonControl(options?.selectButtonClass ?? 'ctrl-select', () => {
this.activate(!this.shiftPressed);
});
this.unselectButton = new ButtonControl(options?.unselectButtonClass ?? 'ctrl-unselect', () => {
this.reset();
this.activate(false);
this.map!.fire('select.end', { features: [] });
});
this.groupElement.appendChild(this.selectButton.element);
this.groupElement.appendChild(this.unselectButton.element);
this.onKeyDownHandler = this.onKeyDown.bind(this);
this.onKeyUpHandler = this.onKeyUp.bind(this);
this.onMouseDownHandler = this.onMouseDown.bind(this);
this.onMouseMoveHandler = this.onMouseMove.bind(this);
this.onMouseUpHandler = this.onMouseUp.bind(this);
}
onAdd(map: Map): HTMLElement {
this.map = map as any;
this.map!.boxZoom.disable();
this.map!.getContainer().appendChild(this.boxElement);
this.map!.getContainer().addEventListener('pointerdown', this.onMouseDownHandler, true);
document.addEventListener('keydown', this.onKeyDownHandler);
document.addEventListener('keyup', this.onKeyUpHandler);
return this.groupElement;
}
onRemove(): void {
this.map!.boxZoom.enable();
this.boxElement.remove();
this.groupElement.remove();
this.map!.getContainer().removeEventListener('pointerdown', this.onMouseDownHandler);
document.removeEventListener('keydown', this.onKeyDownHandler);
document.removeEventListener('keyup', this.onKeyUpHandler);
}
active(): boolean {
return this.shiftPressed || this.selecting;
}
getMousePosition(event: MouseEvent): Point {
const container = this.map!.getContainer();
const rect = container.getBoundingClientRect();
// @ts-ignore
return new Point(event.clientX - rect.left - container.clientLeft, event.clientY - rect.top - container.clientTop);
}
onKeyDown(event: KeyboardEvent): void {
if (event.key == 'Shift') {
this.activate(true);
}
if (event.key == 'Escape') {
this.reset();
this.activate(false);
this.map!.fire('select.end', { features: [] });
}
}
activate(yes: boolean): void {
this.shiftPressed = yes;
this.selectButton.activate(yes);
this.map!.fire(`select.${yes ? 'enable' : 'disable'}`);
}
showUnselect(yes: boolean): void {
this.unselectButton.show(yes);
}
onKeyUp(event: KeyboardEvent): void {
if (event.key == 'Shift') {
this.activate(false);
}
}
onMouseDown(event: MouseEvent): void {
if (!this.shiftPressed) {
return;
}
if (event.button === 0) {
this.selecting = true;
this.map!.dragPan.disable();
this.startPos = this.getMousePosition(event);
this.lastPos = this.startPos;
document.addEventListener('pointermove', this.onMouseMoveHandler);
document.addEventListener('pointerup', this.onMouseUpHandler);
this.map!.fire('select.start');
}
}
onMouseMove(event: MouseEvent): void {
this.lastPos = this.getMousePosition(event);
const minX = Math.min(this.startPos!.x, this.lastPos!.x),
maxX = Math.max(this.startPos!.x, this.lastPos!.x),
minY = Math.min(this.startPos!.y, this.lastPos!.y),
maxY = Math.max(this.startPos!.y, this.lastPos!.y);
const transform = `translate(${minX}px, ${minY}px)`;
const width = maxX - minX + 'px';
const height = maxY - minY + 'px';
this.updateBoxStyle({ transform, width, height });
}
onMouseUp(event: MouseEvent): void {
this.reset();
const features = this.map!.queryRenderedFeatures([this.startPos!, this.lastPos!], {
layers: this.layers,
});
this.map!.fire('select.end', { features, alt: event.altKey });
}
reset(): void {
this.selecting = false;
this.updateBoxStyle({ width: '0', height: '0', transform: '' });
document.removeEventListener('pointermove', this.onMouseMoveHandler);
document.removeEventListener('pointerup', this.onMouseUpHandler);
this.map!.dragPan.enable();
}
updateBoxStyle(style: { width: string; height: string; transform: string }): void {
this.boxElement.style.transform = style.transform;
this.boxElement.style.width = style.width;
this.boxElement.style.height = style.height;
}
}