UNPKG

@antv/g6

Version:

A Graph Visualization Framework in JavaScript

136 lines (111 loc) 3.87 kB
import EventEmitter from '@antv/event-emitter'; import type { FederatedMouseEvent } from '@antv/g'; import { isEqual, isString } from '@antv/util'; import { CommonEvent } from '../constants'; import type { PinchCallback } from './pinch'; import { PinchHandler } from './pinch'; export interface ShortcutOptions {} export type ShortcutKey = string[]; type Handler = (event: any) => void; const lowerCaseKeys = (keys: ShortcutKey) => keys.map((key) => (isString(key) ? key.toLocaleLowerCase() : key)); export class Shortcut { private map: Map<ShortcutKey, Handler> = new Map(); public pinchHandler: PinchHandler | undefined; private boundHandlePinch: PinchCallback = () => {}; private emitter: EventEmitter; private recordKey = new Set<string>(); constructor(emitter: EventEmitter) { this.emitter = emitter; this.bindEvents(); } public bind(key: ShortcutKey, handler: Handler) { if (key.length === 0) return; if (key.includes(CommonEvent.PINCH) && !this.pinchHandler) { this.boundHandlePinch = this.handlePinch.bind(this); this.pinchHandler = new PinchHandler(this.emitter, 'pinchmove', this.boundHandlePinch); } this.map.set(key, handler); } public unbind(key: ShortcutKey, handler?: Handler) { this.map.forEach((h, k) => { if (isEqual(k, key)) { if (!handler || handler === h) this.map.delete(k); } }); } public unbindAll() { this.map.clear(); } public match(key: ShortcutKey) { // 排序 const recordKeyList = lowerCaseKeys(Array.from(this.recordKey)).sort(); const keyList = lowerCaseKeys(key).sort(); return isEqual(recordKeyList, keyList); } private bindEvents() { const { emitter } = this; emitter.on(CommonEvent.KEY_DOWN, this.onKeyDown); emitter.on(CommonEvent.KEY_UP, this.onKeyUp); emitter.on(CommonEvent.WHEEL, this.onWheel); emitter.on(CommonEvent.DRAG, this.onDrag); // 窗口重新获得焦点后清空按键,避免按键状态异常 // Clear the keys when the window regains focus to avoid abnormal key states globalThis.addEventListener?.('focus', this.onFocus); } private onKeyDown = (event: KeyboardEvent) => { if (!event?.key) return; this.recordKey.add(event.key); this.trigger(event); }; private onKeyUp = (event: KeyboardEvent) => { if (!event?.key) return; this.recordKey.delete(event.key); }; private trigger(event: KeyboardEvent) { this.map.forEach((handler, key) => { if (this.match(key)) handler(event); }); } /** * <zh/> 扩展 wheel, drag 操作 * * <en/> Extend wheel, drag operations * @param eventType - event name * @param event - event */ private triggerExtendKey(eventType: CommonEvent, event: unknown) { this.map.forEach((handler, key) => { if (key.includes(eventType)) { if ( isEqual( Array.from(this.recordKey), key.filter((k) => k !== eventType), ) ) { handler(event); } } }); } private onWheel = (event: WheelEvent) => { this.triggerExtendKey(CommonEvent.WHEEL, event); }; private onDrag = (event: FederatedMouseEvent) => { this.triggerExtendKey(CommonEvent.DRAG, event); }; private handlePinch: PinchCallback = (event, options) => { this.triggerExtendKey(CommonEvent.PINCH, { ...event, ...options }); }; private onFocus = () => { this.recordKey.clear(); }; public destroy() { this.unbindAll(); this.emitter.off(CommonEvent.KEY_DOWN, this.onKeyDown); this.emitter.off(CommonEvent.KEY_UP, this.onKeyUp); this.emitter.off(CommonEvent.WHEEL, this.onWheel); this.emitter.off(CommonEvent.DRAG, this.onDrag); this.pinchHandler?.off('pinchmove', this.boundHandlePinch); globalThis.removeEventListener?.('blur', this.onFocus); } }