mirror of https://gitee.com/antv-l7/antv-l7
238 lines
5.8 KiB
TypeScript
238 lines
5.8 KiB
TypeScript
import {
|
|
ILngLat,
|
|
IMapService,
|
|
IPoint,
|
|
IPopup,
|
|
IPopupOption,
|
|
TYPES,
|
|
} from '@antv/l7-core';
|
|
import {
|
|
anchorTranslate,
|
|
anchorType,
|
|
applyAnchorClass,
|
|
bindAll,
|
|
DOM,
|
|
} from '@antv/l7-utils';
|
|
import { EventEmitter } from 'eventemitter3';
|
|
import { Container } from 'inversify';
|
|
|
|
/** colse event */
|
|
|
|
export default class Popup extends EventEmitter implements IPopup {
|
|
private popupOption: IPopupOption;
|
|
private mapsService: IMapService<unknown>;
|
|
private lngLat: ILngLat;
|
|
private content: HTMLElement;
|
|
private closeButton: HTMLElement;
|
|
private timeoutInstance: any;
|
|
private container: HTMLElement;
|
|
private tip: HTMLElement;
|
|
private scene: Container;
|
|
|
|
constructor(cfg?: Partial<IPopupOption>) {
|
|
super();
|
|
this.popupOption = {
|
|
...this.getdefault(),
|
|
...cfg,
|
|
};
|
|
bindAll(['update', 'onClickClose', 'remove'], this);
|
|
}
|
|
|
|
public addTo(scene: Container) {
|
|
this.mapsService = scene.get<IMapService>(TYPES.IMapService);
|
|
this.mapsService.on('camerachange', this.update);
|
|
this.scene = scene;
|
|
this.update();
|
|
if (this.popupOption.closeOnClick) {
|
|
this.timeoutInstance = setTimeout(() => {
|
|
this.mapsService.on('click', this.onClickClose);
|
|
}, 30);
|
|
}
|
|
this.emit('open');
|
|
return this;
|
|
}
|
|
|
|
public close(): void {
|
|
this.remove();
|
|
}
|
|
|
|
public open(): void {
|
|
this.addTo(this.scene);
|
|
}
|
|
|
|
public setHTML(html: string) {
|
|
const frag = window.document.createDocumentFragment();
|
|
const temp = window.document.createElement('body');
|
|
let child: ChildNode | null;
|
|
temp.innerHTML = html;
|
|
while (true) {
|
|
child = temp.firstChild;
|
|
if (!child) {
|
|
break;
|
|
}
|
|
frag.appendChild(child);
|
|
}
|
|
|
|
return this.setDOMContent(frag);
|
|
}
|
|
|
|
public setLnglat(lngLat: ILngLat): this {
|
|
this.lngLat = lngLat as ILngLat;
|
|
if (Array.isArray(lngLat)) {
|
|
this.lngLat = {
|
|
lng: lngLat[0],
|
|
lat: lngLat[1],
|
|
};
|
|
}
|
|
if (this.mapsService) {
|
|
this.mapsService.on('camerachange', this.update);
|
|
}
|
|
this.update();
|
|
return this;
|
|
}
|
|
public getLnglat(): ILngLat {
|
|
return this.lngLat;
|
|
}
|
|
public setText(text: string) {
|
|
return this.setDOMContent(window.document.createTextNode(text));
|
|
}
|
|
|
|
public setMaxWidth(maxWidth: string): this {
|
|
this.popupOption.maxWidth = maxWidth;
|
|
this.update();
|
|
return this;
|
|
}
|
|
|
|
public setDOMContent(htmlNode: ChildNode | DocumentFragment) {
|
|
this.createContent();
|
|
this.content.appendChild(htmlNode);
|
|
this.update();
|
|
return this;
|
|
}
|
|
|
|
// 移除popup
|
|
public remove() {
|
|
if (this.content) {
|
|
this.removeDom(this.content);
|
|
}
|
|
|
|
if (this.container) {
|
|
this.removeDom(this.container);
|
|
delete this.container;
|
|
}
|
|
if (this.mapsService) {
|
|
// TODO: mapbox AMap 事件同步
|
|
this.mapsService.off('camerachange', this.update);
|
|
this.mapsService.off('click', this.onClickClose);
|
|
delete this.mapsService;
|
|
}
|
|
clearTimeout(this.timeoutInstance);
|
|
this.emit('close');
|
|
return this;
|
|
}
|
|
public isOpen() {
|
|
return !!this.mapsService;
|
|
}
|
|
|
|
private createContent() {
|
|
if (this.content) {
|
|
DOM.remove(this.content);
|
|
}
|
|
this.content = DOM.create('div', 'l7-popup-content', this.container);
|
|
if (this.popupOption.closeButton) {
|
|
this.closeButton = DOM.create(
|
|
'button',
|
|
'l7-popup-close-button',
|
|
this.content,
|
|
);
|
|
// this.closeButton.type = 'button';
|
|
this.closeButton.setAttribute('aria-label', 'Close popup');
|
|
this.closeButton.innerHTML = '×';
|
|
this.closeButton.addEventListener('click', this.onClickClose);
|
|
}
|
|
}
|
|
|
|
private creatDom(tagName: string, className: string, container: HTMLElement) {
|
|
const el = window.document.createElement(tagName);
|
|
if (className !== undefined) {
|
|
el.className = className;
|
|
}
|
|
if (container) {
|
|
container.appendChild(el);
|
|
}
|
|
return el;
|
|
}
|
|
|
|
private removeDom(node: ChildNode) {
|
|
if (node.parentNode) {
|
|
node.parentNode.removeChild(node);
|
|
}
|
|
}
|
|
|
|
private getdefault() {
|
|
return {
|
|
closeButton: true,
|
|
closeOnClick: true,
|
|
maxWidth: '240px',
|
|
offsets: [0, 0],
|
|
anchor: anchorType.BOTTOM,
|
|
className: '',
|
|
};
|
|
}
|
|
|
|
private onClickClose(e: Event) {
|
|
if (e.stopPropagation) {
|
|
e.stopPropagation();
|
|
}
|
|
this.remove();
|
|
}
|
|
|
|
private update() {
|
|
const hasPosition = this.lngLat;
|
|
const { className, maxWidth, anchor } = this.popupOption;
|
|
if (!this.mapsService || !hasPosition || !this.content) {
|
|
return;
|
|
}
|
|
const markerContainer = this.mapsService.getMarkerContainer();
|
|
if (!this.container && markerContainer) {
|
|
this.container = this.creatDom(
|
|
'div',
|
|
'l7-popup',
|
|
markerContainer.parentNode as HTMLElement,
|
|
);
|
|
|
|
this.tip = this.creatDom('div', 'l7-popup-tip', this.container);
|
|
this.container.appendChild(this.content);
|
|
if (className) {
|
|
className
|
|
.split(' ')
|
|
.forEach((name) => this.container.classList.add(name));
|
|
}
|
|
this.container.addEventListener('mousedown', (e) => {
|
|
e.stopPropagation();
|
|
});
|
|
this.container.addEventListener('click', (e) => {
|
|
e.stopPropagation();
|
|
});
|
|
}
|
|
if (maxWidth && this.container.style.maxWidth !== maxWidth) {
|
|
this.container.style.maxWidth = maxWidth;
|
|
}
|
|
|
|
this.updatePosition();
|
|
DOM.setTransform(this.container, `${anchorTranslate[anchor]}`);
|
|
applyAnchorClass(this.container, anchor, 'popup');
|
|
}
|
|
|
|
private updatePosition() {
|
|
if (!this.mapsService) {
|
|
return;
|
|
}
|
|
const { lng, lat } = this.lngLat;
|
|
const { offsets } = this.popupOption;
|
|
const pos = this.mapsService.lngLatToContainer([lng, lat]);
|
|
this.container.style.left = pos.x + offsets[0] + 'px';
|
|
this.container.style.top = pos.y - offsets[1] + 'px';
|
|
}
|
|
}
|