Match-id-51ebbbc699c3a4f6a719bab5e48f825b5c65a65b
This commit is contained in:
commit
c7b0520717
|
@ -1,37 +0,0 @@
|
||||||
import {getVNodeProps} from '../dom/DOMInternalKeys';
|
|
||||||
import {resetValue} from '../dom/valueHandler';
|
|
||||||
|
|
||||||
let updateList: Array<any> | null = null;
|
|
||||||
|
|
||||||
// 受控组件值重新赋值
|
|
||||||
function updateValue(target: Element) {
|
|
||||||
const props = getVNodeProps(target);
|
|
||||||
if (props) {
|
|
||||||
resetValue(target, props);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 存储队列中缓存组件
|
|
||||||
export function addValueUpdateList(target: EventTarget): void {
|
|
||||||
if (updateList) {
|
|
||||||
updateList.push(target);
|
|
||||||
} else {
|
|
||||||
updateList = [target];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 判断是否需要重新赋值
|
|
||||||
export function shouldUpdateValue(): boolean {
|
|
||||||
return updateList !== null && updateList.length > 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 从缓存队列中对受控组件进行赋值
|
|
||||||
export function updateControlledValue() {
|
|
||||||
if (!updateList) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
updateList.forEach(item => {
|
|
||||||
updateValue(item);
|
|
||||||
});
|
|
||||||
updateList = null;
|
|
||||||
}
|
|
|
@ -1,132 +0,0 @@
|
||||||
/**
|
|
||||||
* 事件绑定实现,分为绑定委托事件和非委托事件
|
|
||||||
*/
|
|
||||||
import {allDelegatedNativeEvents} from './EventCollection';
|
|
||||||
import {isDocument} from '../dom/utils/Common';
|
|
||||||
import {
|
|
||||||
getNearestVNode,
|
|
||||||
getNonDelegatedListenerMap,
|
|
||||||
} from '../dom/DOMInternalKeys';
|
|
||||||
import {runDiscreteUpdates} from '../renderer/TreeBuilder';
|
|
||||||
import {isMounted} from '../renderer/vnode/VNodeUtils';
|
|
||||||
import {SuspenseComponent} from '../renderer/vnode/VNodeTags';
|
|
||||||
import {handleEventMain} from './HorizonEventMain';
|
|
||||||
import {decorateNativeEvent} from './customEvents/EventFactory';
|
|
||||||
|
|
||||||
const listeningMarker = '_horizonListening' + Math.random().toString(36).slice(4);
|
|
||||||
|
|
||||||
// 触发委托事件
|
|
||||||
function triggerDelegatedEvent(
|
|
||||||
nativeEvtName: string,
|
|
||||||
isCapture: boolean,
|
|
||||||
targetDom: EventTarget,
|
|
||||||
nativeEvent, // 事件对象event
|
|
||||||
) {
|
|
||||||
// 执行之前的调度事件
|
|
||||||
runDiscreteUpdates();
|
|
||||||
|
|
||||||
const nativeEventTarget = nativeEvent.target || nativeEvent.srcElement;
|
|
||||||
let targetVNode = getNearestVNode(nativeEventTarget);
|
|
||||||
|
|
||||||
if (targetVNode !== null) {
|
|
||||||
if (isMounted(targetVNode)) {
|
|
||||||
if (targetVNode.tag === SuspenseComponent) {
|
|
||||||
targetVNode = null;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// vNode已销毁
|
|
||||||
targetVNode = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
handleEventMain(nativeEvtName, isCapture, nativeEvent, targetVNode, targetDom);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 监听委托事件
|
|
||||||
function listenToNativeEvent(
|
|
||||||
nativeEvtName: string,
|
|
||||||
delegatedElement: Element,
|
|
||||||
isCapture: boolean,
|
|
||||||
): void {
|
|
||||||
let dom: Element | Document = delegatedElement;
|
|
||||||
// document层次可能触发selectionchange事件,为了捕获这类事件,selectionchange事件绑定在document节点上
|
|
||||||
if (nativeEvtName === 'selectionchange' && !isDocument(delegatedElement)) {
|
|
||||||
dom = delegatedElement.ownerDocument;
|
|
||||||
}
|
|
||||||
|
|
||||||
const listener = triggerDelegatedEvent.bind(null, nativeEvtName, isCapture, dom);
|
|
||||||
dom.addEventListener(nativeEvtName, listener, isCapture);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 监听所有委托事件
|
|
||||||
export function listenDelegatedEvents(dom: Element) {
|
|
||||||
if (dom[listeningMarker]) {
|
|
||||||
// 不需要重复注册事件
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
dom[listeningMarker] = true;
|
|
||||||
|
|
||||||
allDelegatedNativeEvents.forEach((nativeEvtName: string) => {
|
|
||||||
// 委托冒泡事件
|
|
||||||
listenToNativeEvent(nativeEvtName, dom, false);
|
|
||||||
// 委托捕获事件
|
|
||||||
listenToNativeEvent(nativeEvtName, dom, true);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// 通过horizon事件名获取到native事件名
|
|
||||||
function getNativeEvtName(horizonEventName, capture) {
|
|
||||||
let nativeName;
|
|
||||||
if (capture) {
|
|
||||||
nativeName = horizonEventName.slice(2, -7);
|
|
||||||
} else {
|
|
||||||
nativeName = horizonEventName.slice(2);
|
|
||||||
}
|
|
||||||
if (!nativeName) {
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
return nativeName.toLowerCase();
|
|
||||||
}
|
|
||||||
|
|
||||||
// 是否捕获事件
|
|
||||||
function isCaptureEvent(horizonEventName) {
|
|
||||||
if (horizonEventName === 'onLostPointerCapture' || horizonEventName === 'onGotPointerCapture') {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return horizonEventName.slice(-7) === 'Capture';
|
|
||||||
}
|
|
||||||
|
|
||||||
// 封装监听函数
|
|
||||||
function getWrapperListener(horizonEventName, nativeEvtName, targetElement, listener) {
|
|
||||||
return event => {
|
|
||||||
const customEvent = decorateNativeEvent(horizonEventName, nativeEvtName, event);
|
|
||||||
listener(customEvent);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// 非委托事件单独监听到各自dom节点
|
|
||||||
export function listenNonDelegatedEvent(
|
|
||||||
horizonEventName: string,
|
|
||||||
domElement: Element,
|
|
||||||
listener,
|
|
||||||
): void {
|
|
||||||
const isCapture = isCaptureEvent(horizonEventName);
|
|
||||||
const nativeEvtName = getNativeEvtName(horizonEventName, isCapture);
|
|
||||||
|
|
||||||
// 先判断是否存在老的监听事件,若存在则移除
|
|
||||||
const nonDelegatedListenerMap = getNonDelegatedListenerMap(domElement);
|
|
||||||
const currentListener = nonDelegatedListenerMap.get(horizonEventName);
|
|
||||||
if (currentListener) {
|
|
||||||
domElement.removeEventListener(nativeEvtName, currentListener);
|
|
||||||
nonDelegatedListenerMap.delete(horizonEventName);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (typeof listener !== 'function') {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 为了和委托事件对外行为一致,将事件对象封装成CustomBaseEvent
|
|
||||||
const wrapperListener = getWrapperListener(horizonEventName, nativeEvtName, domElement, listener);
|
|
||||||
// 添加新的监听
|
|
||||||
nonDelegatedListenerMap.set(horizonEventName, wrapperListener);
|
|
||||||
domElement.addEventListener(nativeEvtName, wrapperListener, isCapture);
|
|
||||||
}
|
|
|
@ -1,15 +0,0 @@
|
||||||
import {horizonEventToNativeMap} from './const';
|
|
||||||
|
|
||||||
// 需要委托的horizon事件和原生事件对应关系
|
|
||||||
export const allDelegatedHorizonEvents = new Map();
|
|
||||||
// 所有委托的原生事件集合
|
|
||||||
export const allDelegatedNativeEvents = new Set();
|
|
||||||
|
|
||||||
horizonEventToNativeMap.forEach((dependencies, horizonEvent) => {
|
|
||||||
allDelegatedHorizonEvents.set(horizonEvent, dependencies);
|
|
||||||
allDelegatedHorizonEvents.set(horizonEvent + 'Capture', dependencies);
|
|
||||||
|
|
||||||
dependencies.forEach(d => {
|
|
||||||
allDelegatedNativeEvents.add(d);
|
|
||||||
});
|
|
||||||
});
|
|
|
@ -1,166 +0,0 @@
|
||||||
import type { AnyNativeEvent } from './Types';
|
|
||||||
import type { VNode } from '../renderer/Types';
|
|
||||||
|
|
||||||
import {
|
|
||||||
CommonEventToHorizonMap,
|
|
||||||
horizonEventToNativeMap,
|
|
||||||
EVENT_TYPE_BUBBLE,
|
|
||||||
EVENT_TYPE_CAPTURE,
|
|
||||||
} from './const';
|
|
||||||
import { getListeners as getChangeListeners } from './simulatedEvtHandler/ChangeEventHandler';
|
|
||||||
import { getListeners as getSelectionListeners } from './simulatedEvtHandler/SelectionEventHandler';
|
|
||||||
import {
|
|
||||||
setPropertyWritable,
|
|
||||||
} from './utils';
|
|
||||||
import { decorateNativeEvent } from './customEvents/EventFactory';
|
|
||||||
import { getListenersFromTree } from './ListenerGetter';
|
|
||||||
import { shouldUpdateValue, updateControlledValue } from './ControlledValueUpdater';
|
|
||||||
import { asyncUpdates, runDiscreteUpdates } from '../renderer/Renderer';
|
|
||||||
import { getExactNode } from '../renderer/vnode/VNodeUtils';
|
|
||||||
import {ListenerUnitList} from './Types';
|
|
||||||
|
|
||||||
// 获取事件触发的普通事件监听方法队列
|
|
||||||
function getCommonListeners(
|
|
||||||
nativeEvtName: string,
|
|
||||||
vNode: null | VNode,
|
|
||||||
nativeEvent: AnyNativeEvent,
|
|
||||||
target: null | EventTarget,
|
|
||||||
isCapture: boolean,
|
|
||||||
): ListenerUnitList {
|
|
||||||
const name = CommonEventToHorizonMap[nativeEvtName];
|
|
||||||
const horizonEvtName = !name ? '' : `on${name[0].toUpperCase()}${name.slice(1)}`; // 例:dragEnd -> onDragEnd
|
|
||||||
|
|
||||||
if (!horizonEvtName) {
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
|
|
||||||
// 鼠标点击右键
|
|
||||||
if (nativeEvent instanceof MouseEvent && nativeEvtName === 'click' && nativeEvent.button === 2) {
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (nativeEvtName === 'focusin') {
|
|
||||||
nativeEvtName = 'focus';
|
|
||||||
}
|
|
||||||
|
|
||||||
if (nativeEvtName === 'focusout') {
|
|
||||||
nativeEvtName = 'blur';
|
|
||||||
}
|
|
||||||
|
|
||||||
const horizonEvent = decorateNativeEvent(horizonEvtName, nativeEvtName, nativeEvent);
|
|
||||||
return getListenersFromTree(
|
|
||||||
vNode,
|
|
||||||
horizonEvtName,
|
|
||||||
horizonEvent,
|
|
||||||
isCapture ? EVENT_TYPE_CAPTURE : EVENT_TYPE_BUBBLE,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 按顺序执行事件队列
|
|
||||||
function processListeners(listenerList: ListenerUnitList): void {
|
|
||||||
listenerList.forEach(eventUnit => {
|
|
||||||
const { currentTarget, listener, event } = eventUnit;
|
|
||||||
if (event.isPropagationStopped()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
setPropertyWritable(event, 'currentTarget');
|
|
||||||
event.currentTarget = currentTarget;
|
|
||||||
listener(event);
|
|
||||||
event.currentTarget = null;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function getProcessListeners(
|
|
||||||
nativeEvtName: string,
|
|
||||||
vNode: VNode | null,
|
|
||||||
nativeEvent: AnyNativeEvent,
|
|
||||||
target,
|
|
||||||
isCapture: boolean
|
|
||||||
): ListenerUnitList {
|
|
||||||
// 触发普通委托事件
|
|
||||||
let listenerList: ListenerUnitList = getCommonListeners(
|
|
||||||
nativeEvtName,
|
|
||||||
vNode,
|
|
||||||
nativeEvent,
|
|
||||||
target,
|
|
||||||
isCapture,
|
|
||||||
);
|
|
||||||
|
|
||||||
// 触发特殊handler委托事件
|
|
||||||
if (!isCapture) {
|
|
||||||
if (horizonEventToNativeMap.get('onChange').includes(nativeEvtName)) {
|
|
||||||
listenerList = listenerList.concat(getChangeListeners(
|
|
||||||
nativeEvtName,
|
|
||||||
nativeEvent,
|
|
||||||
vNode,
|
|
||||||
target,
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (horizonEventToNativeMap.get('onSelect').includes(nativeEvtName)) {
|
|
||||||
listenerList = listenerList.concat(getSelectionListeners(
|
|
||||||
nativeEvtName,
|
|
||||||
nativeEvent,
|
|
||||||
vNode,
|
|
||||||
target,
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return listenerList;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 触发可以被执行的horizon事件监听
|
|
||||||
function triggerHorizonEvents(
|
|
||||||
nativeEvtName: string,
|
|
||||||
isCapture: boolean,
|
|
||||||
nativeEvent: AnyNativeEvent,
|
|
||||||
vNode: VNode | null,
|
|
||||||
): void {
|
|
||||||
const nativeEventTarget = nativeEvent.target || nativeEvent.srcElement;
|
|
||||||
|
|
||||||
// 获取委托事件队列
|
|
||||||
const listenerList = getProcessListeners(nativeEvtName, vNode, nativeEvent, nativeEventTarget, isCapture);
|
|
||||||
|
|
||||||
// 处理触发的事件队列
|
|
||||||
processListeners(listenerList);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// 其他事件正在执行中标记
|
|
||||||
let isInEventsExecution = false;
|
|
||||||
|
|
||||||
// 处理委托事件入口
|
|
||||||
export function handleEventMain(
|
|
||||||
nativeEvtName: string,
|
|
||||||
isCapture: boolean,
|
|
||||||
nativeEvent: AnyNativeEvent,
|
|
||||||
vNode: null | VNode,
|
|
||||||
targetContainer: EventTarget,
|
|
||||||
): void {
|
|
||||||
let startVNode = vNode;
|
|
||||||
if (startVNode !== null) {
|
|
||||||
startVNode = getExactNode(startVNode, targetContainer);
|
|
||||||
if (!startVNode) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 有事件正在执行,同步执行事件
|
|
||||||
if (isInEventsExecution) {
|
|
||||||
triggerHorizonEvents(nativeEvtName, isCapture, nativeEvent, startVNode);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 没有事件在执行,经过调度再执行事件
|
|
||||||
isInEventsExecution = true;
|
|
||||||
try {
|
|
||||||
asyncUpdates(() => triggerHorizonEvents(nativeEvtName, isCapture, nativeEvent, startVNode));
|
|
||||||
} finally {
|
|
||||||
isInEventsExecution = false;
|
|
||||||
if (shouldUpdateValue()) {
|
|
||||||
runDiscreteUpdates();
|
|
||||||
updateControlledValue();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,72 +0,0 @@
|
||||||
import { VNode } from '../renderer/Types';
|
|
||||||
import { DomComponent } from '../renderer/vnode/VNodeTags';
|
|
||||||
import { EVENT_TYPE_ALL, EVENT_TYPE_CAPTURE, EVENT_TYPE_BUBBLE } from './const';
|
|
||||||
import { AnyNativeEvent, ListenerUnitList } from './Types';
|
|
||||||
|
|
||||||
// 从vnode属性中获取事件listener
|
|
||||||
function getListenerFromVNode(vNode: VNode, eventName: string): Function | null {
|
|
||||||
const props = vNode.props;
|
|
||||||
const mouseEvents = ['onClick', 'onDoubleClick', 'onMouseDown', 'onMouseMove', 'onMouseUp', 'onMouseEnter'];
|
|
||||||
const formElements = ['button', 'input', 'select', 'textarea'];
|
|
||||||
|
|
||||||
// 是否应该阻止禁用的表单元素触发鼠标事件
|
|
||||||
const shouldPreventMouseEvent =
|
|
||||||
mouseEvents.includes(eventName) && props.disabled && formElements.includes(vNode.type);
|
|
||||||
|
|
||||||
const listener = props[eventName];
|
|
||||||
if (shouldPreventMouseEvent) {
|
|
||||||
return null;
|
|
||||||
} else {
|
|
||||||
return listener;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 获取监听事件
|
|
||||||
export function getListenersFromTree(
|
|
||||||
targetVNode: VNode | null,
|
|
||||||
horizonEvtName: string | null,
|
|
||||||
nativeEvent: AnyNativeEvent,
|
|
||||||
eventType: string
|
|
||||||
): ListenerUnitList {
|
|
||||||
if (!horizonEvtName) {
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
|
|
||||||
const listeners: ListenerUnitList = [];
|
|
||||||
|
|
||||||
let vNode = targetVNode;
|
|
||||||
|
|
||||||
// 从目标节点到根节点遍历获取listener
|
|
||||||
while (vNode !== null) {
|
|
||||||
const { realNode, tag } = vNode;
|
|
||||||
if (tag === DomComponent && realNode !== null) {
|
|
||||||
if (eventType === EVENT_TYPE_ALL || eventType === EVENT_TYPE_CAPTURE) {
|
|
||||||
const captureName = horizonEvtName + EVENT_TYPE_CAPTURE;
|
|
||||||
const captureListener = getListenerFromVNode(vNode, captureName);
|
|
||||||
if (captureListener) {
|
|
||||||
listeners.unshift({
|
|
||||||
vNode,
|
|
||||||
listener: captureListener,
|
|
||||||
currentTarget: realNode,
|
|
||||||
event: nativeEvent,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (eventType === EVENT_TYPE_ALL || eventType === EVENT_TYPE_BUBBLE) {
|
|
||||||
const bubbleListener = getListenerFromVNode(vNode, horizonEvtName);
|
|
||||||
if (bubbleListener) {
|
|
||||||
listeners.push({
|
|
||||||
vNode,
|
|
||||||
listener: bubbleListener,
|
|
||||||
currentTarget: realNode,
|
|
||||||
event: nativeEvent,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
vNode = vNode.parent;
|
|
||||||
}
|
|
||||||
|
|
||||||
return listeners;
|
|
||||||
}
|
|
|
@ -1,13 +0,0 @@
|
||||||
|
|
||||||
import type {VNode} from '../renderer/Types';
|
|
||||||
|
|
||||||
export type AnyNativeEvent = KeyboardEvent | MouseEvent | TouchEvent | UIEvent | Event;
|
|
||||||
|
|
||||||
export type ListenerUnit = {
|
|
||||||
vNode: null | VNode;
|
|
||||||
listener: Function;
|
|
||||||
currentTarget: EventTarget;
|
|
||||||
event: AnyNativeEvent;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type ListenerUnitList = Array<ListenerUnit>;
|
|
|
@ -1,79 +0,0 @@
|
||||||
|
|
||||||
// Horizon事件和原生事件对应关系
|
|
||||||
export const horizonEventToNativeMap = new Map([
|
|
||||||
['onKeyPress', ['keypress']],
|
|
||||||
['onTextInput', ['textInput']],
|
|
||||||
['onClick', ['click']],
|
|
||||||
['onDoubleClick', ['dblclick']],
|
|
||||||
['onFocus', ['focusin']],
|
|
||||||
['onBlur', ['focusout']],
|
|
||||||
['onInput', ['input']],
|
|
||||||
['onMouseOut', ['mouseout']],
|
|
||||||
['onMouseOver', ['mouseover']],
|
|
||||||
['onPointerOut', ['pointerout']],
|
|
||||||
['onPointerOver', ['pointerover']],
|
|
||||||
['onContextMenu', ['contextmenu']],
|
|
||||||
['onDragEnd', ['dragend']],
|
|
||||||
['onKeyDown', ['keydown']],
|
|
||||||
['onKeyUp', ['keyup']],
|
|
||||||
['onMouseDown', ['mousedown']],
|
|
||||||
['onMouseMove', ['mousemove']],
|
|
||||||
['onMouseUp', ['mouseup']],
|
|
||||||
['onSelectChange', ['selectionchange']],
|
|
||||||
['onTouchEnd', ['touchend']],
|
|
||||||
['onTouchMove', ['touchmove']],
|
|
||||||
['onTouchStart', ['touchstart']],
|
|
||||||
|
|
||||||
['onCompositionEnd', ['compositionend']],
|
|
||||||
['onCompositionStart', ['compositionstart']],
|
|
||||||
['onCompositionUpdate', ['compositionupdate']],
|
|
||||||
['onChange', ['change', 'click', 'focusout', 'input']],
|
|
||||||
['onSelect', ['focusout', 'contextmenu', 'dragend', 'focusin',
|
|
||||||
'keydown', 'keyup', 'mousedown', 'mouseup', 'selectionchange']],
|
|
||||||
|
|
||||||
['onAnimationEnd', ['animationend']],
|
|
||||||
['onAnimationIteration', ['animationiteration']],
|
|
||||||
['onAnimationStart', ['animationstart']],
|
|
||||||
['onTransitionEnd', ['transitionend']]
|
|
||||||
]);
|
|
||||||
|
|
||||||
export const CommonEventToHorizonMap = {
|
|
||||||
click: 'click',
|
|
||||||
dblclick: 'doubleClick',
|
|
||||||
contextmenu: 'contextMenu',
|
|
||||||
dragend: 'dragEnd',
|
|
||||||
focusin: 'focus',
|
|
||||||
focusout: 'blur',
|
|
||||||
input: 'input',
|
|
||||||
keydown: 'keyDown',
|
|
||||||
keypress: 'keyPress',
|
|
||||||
keyup: 'keyUp',
|
|
||||||
mousedown: 'mouseDown',
|
|
||||||
mouseup: 'mouseUp',
|
|
||||||
touchend: 'touchEnd',
|
|
||||||
touchstart: 'touchStart',
|
|
||||||
mousemove: 'mouseMove',
|
|
||||||
mouseout: 'mouseOut',
|
|
||||||
mouseover: 'mouseOver',
|
|
||||||
pointermove: 'pointerMove',
|
|
||||||
pointerout: 'pointerOut',
|
|
||||||
pointerover: 'pointerOver',
|
|
||||||
selectionchange: 'selectChange',
|
|
||||||
textInput: 'textInput',
|
|
||||||
touchmove: 'touchMove',
|
|
||||||
animationend: 'animationEnd',
|
|
||||||
animationiteration: 'animationIteration',
|
|
||||||
animationstart: 'animationStart',
|
|
||||||
transitionend: 'transitionEnd',
|
|
||||||
compositionstart: 'compositionStart',
|
|
||||||
compositionend: 'compositionEnd',
|
|
||||||
compositionupdate: 'compositionUpdate',
|
|
||||||
};
|
|
||||||
|
|
||||||
export const CHAR_CODE_SPACE = 32;
|
|
||||||
|
|
||||||
|
|
||||||
export const EVENT_TYPE_BUBBLE = 'Bubble';
|
|
||||||
export const EVENT_TYPE_CAPTURE = 'Capture';
|
|
||||||
export const EVENT_TYPE_ALL = 'All';
|
|
||||||
|
|
|
@ -1,36 +0,0 @@
|
||||||
|
|
||||||
// 兼容IE的event key
|
|
||||||
const uniqueKeyMap = new Map([
|
|
||||||
['Esc', 'Escape'],
|
|
||||||
['Spacebar', ' '],
|
|
||||||
['Left', 'ArrowLeft'],
|
|
||||||
['Up', 'ArrowUp'],
|
|
||||||
['Right', 'ArrowRight'],
|
|
||||||
['Down', 'ArrowDown'],
|
|
||||||
['Del', 'Delete'],
|
|
||||||
]);
|
|
||||||
|
|
||||||
const noop = () => {};
|
|
||||||
// 创建普通自定义事件对象实例,和原生事件对应
|
|
||||||
export function decorateNativeEvent(customEventName, nativeEvtName, nativeEvent) {
|
|
||||||
|
|
||||||
nativeEvent.isDefaultPrevented = () => nativeEvent.defaultPrevented;
|
|
||||||
nativeEvent.isPropagationStopped = () => nativeEvent.cancelBubble;
|
|
||||||
// 适配老版本事件api
|
|
||||||
nativeEvent.persist = noop;
|
|
||||||
|
|
||||||
// custom事件自定义属性
|
|
||||||
nativeEvent.customEventName = customEventName;
|
|
||||||
nativeEvent.nativeEvent = nativeEvent;
|
|
||||||
// 保存原生的事件类型,因为下面会修改
|
|
||||||
nativeEvent.nativeEventType = nativeEvent.type;
|
|
||||||
|
|
||||||
Object.defineProperty(nativeEvent, 'type', { writable: true });
|
|
||||||
nativeEvent.type = nativeEvtName;
|
|
||||||
|
|
||||||
const orgKey = nativeEvent.key;
|
|
||||||
Object.defineProperty(nativeEvent, 'key', { writable: true });
|
|
||||||
nativeEvent.key = uniqueKeyMap.get(orgKey) || orgKey;
|
|
||||||
|
|
||||||
return nativeEvent;
|
|
||||||
}
|
|
|
@ -1,60 +0,0 @@
|
||||||
import {decorateNativeEvent} from '../customEvents/EventFactory';
|
|
||||||
import {getDom} from '../../dom/DOMInternalKeys';
|
|
||||||
import {isInputValueChanged} from '../../dom/valueHandler/ValueChangeHandler';
|
|
||||||
import {addValueUpdateList} from '../ControlledValueUpdater';
|
|
||||||
import {isInputElement} from '../utils';
|
|
||||||
import {EVENT_TYPE_ALL} from '../const';
|
|
||||||
import {AnyNativeEvent, ListenerUnitList} from '../Types';
|
|
||||||
import {
|
|
||||||
getListenersFromTree,
|
|
||||||
} from '../ListenerGetter';
|
|
||||||
import {VNode} from '../../renderer/Types';
|
|
||||||
import {getDomTag} from '../../dom/utils/Common';
|
|
||||||
|
|
||||||
// 返回是否需要触发change事件标记
|
|
||||||
function shouldTriggerChangeEvent(targetDom, evtName) {
|
|
||||||
const { type } = targetDom;
|
|
||||||
const domTag = getDomTag(targetDom);
|
|
||||||
|
|
||||||
if (domTag === 'select' || (domTag === 'input' && type === 'file')) {
|
|
||||||
return evtName === 'change';
|
|
||||||
} else if (domTag === 'input' && (type === 'checkbox' || type === 'radio')) {
|
|
||||||
if (evtName === 'click') {
|
|
||||||
return isInputValueChanged(targetDom);
|
|
||||||
}
|
|
||||||
} else if (isInputElement(targetDom)) {
|
|
||||||
if (evtName === 'input' || evtName === 'change') {
|
|
||||||
return isInputValueChanged(targetDom);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* 支持input/textarea/select的onChange事件
|
|
||||||
*/
|
|
||||||
export function getListeners(
|
|
||||||
nativeEvtName: string,
|
|
||||||
nativeEvt: AnyNativeEvent,
|
|
||||||
vNode: null | VNode,
|
|
||||||
target: null | EventTarget,
|
|
||||||
): ListenerUnitList {
|
|
||||||
if (!vNode) {
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
const targetDom = getDom(vNode);
|
|
||||||
|
|
||||||
// 判断是否需要触发change事件
|
|
||||||
if (shouldTriggerChangeEvent(targetDom, nativeEvtName)) {
|
|
||||||
addValueUpdateList(target);
|
|
||||||
const event = decorateNativeEvent(
|
|
||||||
'onChange',
|
|
||||||
'change',
|
|
||||||
nativeEvt,
|
|
||||||
);
|
|
||||||
return getListenersFromTree(vNode, 'onChange', event, EVENT_TYPE_ALL);
|
|
||||||
}
|
|
||||||
|
|
||||||
return [];
|
|
||||||
}
|
|
|
@ -1,112 +0,0 @@
|
||||||
import {decorateNativeEvent} from '../customEvents/EventFactory';
|
|
||||||
import {shallowCompare} from '../../renderer/utils/compare';
|
|
||||||
import {getFocusedDom} from '../../dom/utils/Common';
|
|
||||||
import {getDom} from '../../dom/DOMInternalKeys';
|
|
||||||
import {isDocument} from '../../dom/utils/Common';
|
|
||||||
import {isInputElement, setPropertyWritable} from '../utils';
|
|
||||||
import type {AnyNativeEvent} from '../Types';
|
|
||||||
import {getListenersFromTree} from '../ListenerGetter';
|
|
||||||
import type {VNode} from '../../renderer/Types';
|
|
||||||
import {EVENT_TYPE_ALL} from '../const';
|
|
||||||
import {ListenerUnitList} from '../Types';
|
|
||||||
|
|
||||||
const horizonEventName = 'onSelect';
|
|
||||||
|
|
||||||
let currentElement = null;
|
|
||||||
let currentVNode = null;
|
|
||||||
let lastSelection: Selection | null = null;
|
|
||||||
|
|
||||||
function initTargetCache(dom, vNode) {
|
|
||||||
if (isInputElement(dom) || dom.contentEditable === 'true') {
|
|
||||||
currentElement = dom;
|
|
||||||
currentVNode = vNode;
|
|
||||||
lastSelection = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function clearTargetCache() {
|
|
||||||
currentElement = null;
|
|
||||||
currentVNode = null;
|
|
||||||
lastSelection = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 标记是否在鼠标事件过程中
|
|
||||||
let isInMouseEvent = false;
|
|
||||||
|
|
||||||
// 获取节点所在的document对象
|
|
||||||
function getDocument(eventTarget) {
|
|
||||||
if (eventTarget.window === eventTarget) {
|
|
||||||
return eventTarget.document;
|
|
||||||
}
|
|
||||||
if (isDocument(eventTarget)) {
|
|
||||||
return eventTarget;
|
|
||||||
}
|
|
||||||
return eventTarget.ownerDocument;
|
|
||||||
}
|
|
||||||
|
|
||||||
function getSelectEvent(nativeEvent, target) {
|
|
||||||
const doc = getDocument(target);
|
|
||||||
if (isInMouseEvent || currentElement == null || currentElement !== getFocusedDom(doc)) {
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
|
|
||||||
const currentSelection = window.getSelection();
|
|
||||||
if (!shallowCompare(lastSelection, currentSelection)) {
|
|
||||||
lastSelection = currentSelection;
|
|
||||||
|
|
||||||
const event = decorateNativeEvent(
|
|
||||||
horizonEventName,
|
|
||||||
'select',
|
|
||||||
nativeEvent,
|
|
||||||
);
|
|
||||||
setPropertyWritable(nativeEvent, 'target');
|
|
||||||
event.target = currentElement;
|
|
||||||
|
|
||||||
return getListenersFromTree(
|
|
||||||
currentVNode,
|
|
||||||
horizonEventName,
|
|
||||||
event,
|
|
||||||
EVENT_TYPE_ALL
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 该插件创建一个onSelect事件
|
|
||||||
* 支持元素: input、textarea、contentEditable元素
|
|
||||||
* 触发场景:用户输入、折叠选择、文本选择
|
|
||||||
*/
|
|
||||||
export function getListeners(
|
|
||||||
nativeEvtName: string,
|
|
||||||
nativeEvt: AnyNativeEvent,
|
|
||||||
vNode: null | VNode,
|
|
||||||
target: null | EventTarget,
|
|
||||||
): ListenerUnitList {
|
|
||||||
const targetNode = vNode ? getDom(vNode) : window;
|
|
||||||
let eventUnitList: ListenerUnitList = [];
|
|
||||||
switch (nativeEvtName) {
|
|
||||||
case 'focusin':
|
|
||||||
initTargetCache(targetNode, vNode);
|
|
||||||
break;
|
|
||||||
case 'focusout':
|
|
||||||
clearTargetCache();
|
|
||||||
break;
|
|
||||||
case 'mousedown':
|
|
||||||
isInMouseEvent = true;
|
|
||||||
break;
|
|
||||||
case 'contextmenu':
|
|
||||||
case 'mouseup':
|
|
||||||
case 'dragend':
|
|
||||||
isInMouseEvent = false;
|
|
||||||
eventUnitList = getSelectEvent(nativeEvt, target);
|
|
||||||
break;
|
|
||||||
case 'selectionchange':
|
|
||||||
case 'keydown':
|
|
||||||
case 'keyup':
|
|
||||||
eventUnitList = getSelectEvent(nativeEvt, target);
|
|
||||||
}
|
|
||||||
|
|
||||||
return eventUnitList;
|
|
||||||
}
|
|
|
@ -1,14 +0,0 @@
|
||||||
|
|
||||||
export function isInputElement(dom?: HTMLElement): boolean {
|
|
||||||
if (dom instanceof HTMLInputElement || dom instanceof HTMLTextAreaElement) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function setPropertyWritable(obj, propName) {
|
|
||||||
const desc = Object.getOwnPropertyDescriptor(obj, propName);
|
|
||||||
if (!desc || !desc.writable) {
|
|
||||||
Object.defineProperty(obj, propName, { writable : true });
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
Reference in New Issue