mapbox-gl
Version:
A WebGL interactive maps library
96 lines (82 loc) • 3.56 kB
JavaScript
// @flow strict
import Point from '@mapbox/point-geometry';
import assert from 'assert';
// refine the return type based on tagName, e.g. 'button' -> HTMLButtonElement
// $FlowFixMe[method-unbinding]
export function create<T: string>(tagName: T, className: ?string, container?: HTMLElement): $Call<typeof document.createElement, T> {
const el = document.createElement(tagName);
if (className !== undefined && className !== null) el.className = className;
if (container) container.appendChild(el);
return el;
}
export function createSVG(tagName: string, attributes: {[string]: string | number}, container?: Element): Element {
const el = document.createElementNS('http://www.w3.org/2000/svg', tagName);
for (const name of Object.keys(attributes)) {
el.setAttributeNS(null, name, String(attributes[name]));
}
if (container) container.appendChild(el);
return el;
}
const docStyle = typeof document !== 'undefined' ? document.documentElement && document.documentElement.style : null;
const selectProp = docStyle && docStyle.userSelect !== undefined ? 'userSelect' : 'WebkitUserSelect';
let userSelect;
export function disableDrag() {
if (docStyle && selectProp) {
// $FlowFixMe[incompatible-type]
userSelect = docStyle[selectProp];
// $FlowFixMe[incompatible-type]
docStyle[selectProp] = 'none';
}
}
export function enableDrag() {
if (docStyle && selectProp) {
// $FlowFixMe[incompatible-type]
docStyle[selectProp] = userSelect;
}
}
// Suppress the next click, but only if it's immediate.
function suppressClickListener(e: Event) {
e.preventDefault();
e.stopPropagation();
window.removeEventListener('click', suppressClickListener, true);
}
export function suppressClick() {
window.addEventListener('click', suppressClickListener, true);
window.setTimeout(() => {
window.removeEventListener('click', suppressClickListener, true);
}, 0);
}
export function mousePos(el: HTMLElement, e: MouseEvent | WheelEvent): Point {
const rect = el.getBoundingClientRect();
return getScaledPoint(el, rect, e);
}
export function touchPos(el: HTMLElement, touches: TouchList): Array<Point> {
const rect = el.getBoundingClientRect(),
points = [];
for (let i = 0; i < touches.length; i++) {
points.push(getScaledPoint(el, rect, touches[i]));
}
return points;
}
export function mouseButton(e: MouseEvent): number {
assert(e.type === 'mousedown' || e.type === 'mouseup');
if (typeof window.InstallTrigger !== 'undefined' && e.button === 2 && e.ctrlKey &&
window.navigator.platform.toUpperCase().indexOf('MAC') >= 0) {
// Fix for https://github.com/mapbox/mapbox-gl-js/issues/3131:
// Firefox (detected by InstallTrigger) on Mac determines e.button = 2 when
// using Control + left click
return 0;
}
return e.button;
}
function getScaledPoint(el: HTMLElement, rect: ClientRect, e: MouseEvent | WheelEvent | Touch) {
// Until we get support for pointer events (https://developer.mozilla.org/en-US/docs/Web/API/PointerEvent)
// we use this dirty trick which would not work for the case of rotated transforms, but works well for
// the case of simple scaling.
// Note: `el.offsetWidth === rect.width` eliminates the `0/0` case.
const scaling = el.offsetWidth === rect.width ? 1 : el.offsetWidth / rect.width;
return new Point(
(e.clientX - rect.left) * scaling,
(e.clientY - rect.top) * scaling
);
}