jjb-lc-designable
Version:
基于alibaba-designable源码二次封装的表单设计器。
200 lines • 7.03 kB
JavaScript
import { isArr, isWindow } from './types';
import { Subscribable } from './subscribable';
import { globalThisPolyfill } from './globalThisPolyfill';
const ATTACHED_SYMBOL = Symbol('ATTACHED_SYMBOL');
const EVENTS_SYMBOL = Symbol('__EVENTS_SYMBOL__');
const EVENTS_ONCE_SYMBOL = Symbol('EVENTS_ONCE_SYMBOL');
const EVENTS_BATCH_SYMBOL = Symbol('EVENTS_BATCH_SYMBOL');
const DRIVER_INSTANCES_SYMBOL = Symbol('DRIVER_INSTANCES_SYMBOL');
const isOnlyMode = mode => mode === 'onlyOne' || mode === 'onlyChild' || mode === 'onlyParent';
/**
* 事件驱动器基类
*/
export class EventDriver {
container = document;
contentWindow = globalThisPolyfill;
constructor(engine, context) {
this.engine = engine;
this.context = context;
}
dispatch(event) {
return this.engine.dispatch(event, this.context);
}
subscribe(subscriber) {
return this.engine.subscribe(subscriber);
}
subscribeTo(type, subscriber) {
return this.engine.subscribeTo(type, subscriber);
}
subscribeWith(type, subscriber) {
return this.engine.subscribeWith(type, subscriber);
}
attach(container) {
console.error('attach must implement.');
}
detach(container) {
console.error('attach must implement.');
}
eventTarget(type) {
if (type === 'resize' || type === 'scroll') {
if (this.container === this.contentWindow?.document) {
return this.contentWindow;
}
}
return this.container;
}
addEventListener(type, listener, options) {
const target = this.eventTarget(type);
if (isOnlyMode(options?.mode)) {
target[EVENTS_ONCE_SYMBOL] = target[EVENTS_ONCE_SYMBOL] || {};
const constructor = this['constructor'];
constructor[EVENTS_ONCE_SYMBOL] = constructor[EVENTS_ONCE_SYMBOL] || {};
const handler = target[EVENTS_ONCE_SYMBOL][type];
const container = constructor[EVENTS_ONCE_SYMBOL][type];
if (!handler) {
if (container) {
if (options.mode === 'onlyChild') {
if (container.contains(target)) {
container.removeEventListener(type, container[EVENTS_ONCE_SYMBOL][type], options);
delete container[EVENTS_ONCE_SYMBOL][type];
}
} else if (options.mode === 'onlyParent') {
if (container.contains(target)) return;
}
}
target.addEventListener(type, listener, options);
target[EVENTS_ONCE_SYMBOL][type] = listener;
constructor[EVENTS_ONCE_SYMBOL][type] = target;
}
} else {
target[EVENTS_SYMBOL] = target[EVENTS_SYMBOL] || {};
target[EVENTS_SYMBOL][type] = target[EVENTS_SYMBOL][type] || new Map();
if (!target[EVENTS_SYMBOL][type]?.get?.(listener)) {
target.addEventListener(type, listener, options);
target[EVENTS_SYMBOL][type]?.set?.(listener, true);
}
}
}
removeEventListener(type, listener, options) {
const target = this.eventTarget(type);
if (isOnlyMode(options?.mode)) {
const constructor = this['constructor'];
constructor[EVENTS_ONCE_SYMBOL] = constructor[EVENTS_ONCE_SYMBOL] || {};
target[EVENTS_ONCE_SYMBOL] = target[EVENTS_ONCE_SYMBOL] || {};
delete constructor[EVENTS_ONCE_SYMBOL][type];
delete target[EVENTS_ONCE_SYMBOL][type];
target.removeEventListener(type, listener, options);
} else {
target[EVENTS_SYMBOL] = target[EVENTS_SYMBOL] || {};
target[EVENTS_SYMBOL][type] = target[EVENTS_SYMBOL][type] || new Map();
target[EVENTS_SYMBOL][type]?.delete?.(listener);
target.removeEventListener(type, listener, options);
}
}
batchAddEventListener(type, listener, options) {
this.engine[DRIVER_INSTANCES_SYMBOL] = this.engine[DRIVER_INSTANCES_SYMBOL] || [];
if (!this.engine[DRIVER_INSTANCES_SYMBOL].includes(this)) {
this.engine[DRIVER_INSTANCES_SYMBOL].push(this);
}
this.engine[DRIVER_INSTANCES_SYMBOL].forEach(driver => {
const target = driver.eventTarget(type);
target[EVENTS_BATCH_SYMBOL] = target[EVENTS_BATCH_SYMBOL] || {};
if (!target[EVENTS_BATCH_SYMBOL][type]) {
target.addEventListener(type, listener, options);
target[EVENTS_BATCH_SYMBOL][type] = listener;
}
});
}
batchRemoveEventListener(type, listener, options) {
this.engine[DRIVER_INSTANCES_SYMBOL] = this.engine[DRIVER_INSTANCES_SYMBOL] || [];
this.engine[DRIVER_INSTANCES_SYMBOL].forEach(driver => {
const target = driver.eventTarget(type);
target[EVENTS_BATCH_SYMBOL] = target[EVENTS_BATCH_SYMBOL] || {};
target.removeEventListener(type, listener, options);
delete target[EVENTS_BATCH_SYMBOL][type];
});
}
}
/**
* 事件引擎
*/
export class Event extends Subscribable {
drivers = [];
containers = [];
constructor(props) {
super();
if (isArr(props?.effects)) {
props.effects.forEach(plugin => {
plugin(this);
});
}
if (isArr(props?.drivers)) {
this.drivers = props.drivers;
}
}
subscribeTo(type, subscriber) {
return this.subscribe(event => {
if (type && event instanceof type) {
return subscriber(event);
}
});
}
subscribeWith(type, subscriber) {
return this.subscribe(event => {
if (isArr(type)) {
if (type.includes(event?.type)) {
return subscriber(event);
}
} else {
if (type && event?.type === type) {
return subscriber(event);
}
}
});
}
attachEvents(container, contentWindow = globalThisPolyfill, context) {
if (!container) return;
if (isWindow(container)) {
return this.attachEvents(container.document, container, context);
}
if (container[ATTACHED_SYMBOL]) return;
container[ATTACHED_SYMBOL] = this.drivers.map(EventDriver => {
const driver = new EventDriver(this, context);
driver.contentWindow = contentWindow;
driver.container = container;
driver.attach(container);
return driver;
});
if (!this.containers.includes(container)) {
this.containers.push(container);
}
}
detachEvents(container) {
if (!container) {
this.containers.forEach(container => {
this.detachEvents(container);
});
return;
}
if (isWindow(container)) {
return this.detachEvents(container.document);
}
if (!container[ATTACHED_SYMBOL]) return;
container[ATTACHED_SYMBOL].forEach(driver => {
driver.detach(container);
});
this[DRIVER_INSTANCES_SYMBOL] = this[DRIVER_INSTANCES_SYMBOL] || [];
this[DRIVER_INSTANCES_SYMBOL] = this[DRIVER_INSTANCES_SYMBOL].reduce((drivers, driver) => {
if (driver.container === container) {
driver.detach(container);
return drivers;
}
return drivers.concat(driver);
}, []);
this.containers = this.containers.filter(item => item !== container);
delete container[ATTACHED_SYMBOL];
delete container[EVENTS_SYMBOL];
delete container[EVENTS_ONCE_SYMBOL];
delete container[EVENTS_BATCH_SYMBOL];
}
}