Match-id-a6d1e485b0c777920dac79e31e91f3f0798eb2fe

This commit is contained in:
* 2021-12-25 11:55:43 +08:00 committed by *
parent d76fcbb8ff
commit 92f1ba7db2
7 changed files with 0 additions and 501 deletions

View File

@ -1,24 +0,0 @@
// 处理事件的错误
let hasError = false;
let caughtError = null;
// 执行事件监听器,并且捕捉第一个错误,事件执行完成后抛出第一个错误
export function runListenerAndCatchFirstError(listener, event) {
try {
listener(event);
} catch (error) {
if (!hasError) {
hasError = true;
caughtError = error;
}
}
}
export function throwCaughtEventError() {
if (hasError) {
const err = caughtError;
caughtError = null;
hasError = false;
throw err;
}
}

View File

@ -1,201 +0,0 @@
import type {AnyNativeEvent, ProcessingListenerList} from './types';
import type {VNode} from '../renderer/Types';
import {
CommonEventToHorizonMap,
horizonEventToNativeMap,
EVENT_TYPE_BUBBLE,
EVENT_TYPE_CAPTURE,
} from './const';
import {
throwCaughtEventError,
runListenerAndCatchFirstError,
} from './EventError';
import {getListeners as getBeforeInputListeners} from './simulatedEvtHandler/BeforeInputEventHandler';
import {getListeners as getCompositionListeners} from './simulatedEvtHandler/CompositionEventHandler';
import {getListeners as getChangeListeners} from './simulatedEvtHandler/ChangeEventHandler';
import {getListeners as getSelectionListeners} from './simulatedEvtHandler/SelectionEventHandler';
import {
getCustomEventNameWithOn,
uniqueCharCode,
getEventTarget
} from './utils';
import {createCommonCustomEvent} from './customEvents/EventFactory';
import {getListenersFromTree} from './ListenerGetter';
import {shouldUpdateValue, updateControlledValue} from './ControlledValueUpdater';
import {asyncUpdates, runDiscreteUpdates} from '../renderer/Renderer';
import {getExactNode} from '../renderer/vnode/VNodeUtils';
// 获取事件触发的普通事件监听方法队列
function getCommonListeners(
nativeEvtName: string,
vNode: null | VNode,
nativeEvent: AnyNativeEvent,
target: null | EventTarget,
isCapture: boolean,
): ProcessingListenerList {
const customEventName = getCustomEventNameWithOn(CommonEventToHorizonMap[nativeEvtName]);
if (!customEventName) {
return [];
}
// 火狐浏览器兼容。火狐浏览器下功能键将触发keypress事件 火狐下keypress的charcode有值keycode为0
if (nativeEvtName === 'keypress' && uniqueCharCode(nativeEvent) === 0) {
return [];
}
// 鼠标点击右键
if (nativeEvent instanceof MouseEvent && nativeEvtName === 'click' && nativeEvent.button === 2) {
return [];
}
if (nativeEvtName === 'focusin') {
nativeEvtName = 'focus';
}
if (nativeEvtName === 'focusout') {
nativeEvtName = 'blur';
}
const customEvent = createCommonCustomEvent(customEventName, nativeEvtName, nativeEvent, null, target);
return getListenersFromTree(
vNode,
customEventName,
customEvent,
isCapture ? EVENT_TYPE_CAPTURE: EVENT_TYPE_BUBBLE,
);
}
// 按顺序执行事件队列
export function processListeners(
processingEventsList: ProcessingListenerList
): void {
processingEventsList.forEach(eventUnitList => {
let lastVNode;
eventUnitList.forEach(eventUnit => {
const {vNode, currentTarget, listener, event} = eventUnit;
if (vNode !== lastVNode && event.isPropagationStopped()) {
return;
}
event.currentTarget = currentTarget;
runListenerAndCatchFirstError(listener, event);
event.currentTarget = null;
lastVNode = vNode;
});
});
// 执行所有事件后重新throw遇到的第一个错误
throwCaughtEventError();
}
function getProcessListenersFacade(
nativeEvtName: string,
vNode: VNode,
nativeEvent: AnyNativeEvent,
target,
isCapture: boolean
): ProcessingListenerList {
// 触发普通委托事件
let processingListenerList: ProcessingListenerList = getCommonListeners(
nativeEvtName,
vNode,
nativeEvent,
target,
isCapture,
);
// 触发特殊handler委托事件
if (!isCapture) {
if (horizonEventToNativeMap.get('onChange').includes(nativeEvtName)) {
processingListenerList = processingListenerList.concat(getChangeListeners(
nativeEvtName,
nativeEvent,
vNode,
target,
));
}
if (horizonEventToNativeMap.get('onSelect').includes(nativeEvtName)) {
processingListenerList = processingListenerList.concat(getSelectionListeners(
nativeEvtName,
nativeEvent,
vNode,
target,
));
}
if (nativeEvtName === 'compositionend' || nativeEvtName === 'compositionstart' || nativeEvtName === 'compositionupdate') {
processingListenerList = processingListenerList.concat(getCompositionListeners(
nativeEvtName,
nativeEvent,
vNode,
target,
));
}
if (horizonEventToNativeMap.get('onBeforeInput').includes(nativeEvtName)) {
processingListenerList = processingListenerList.concat(getBeforeInputListeners(
nativeEvtName,
nativeEvent,
vNode,
target,
));
}
}
return processingListenerList;
}
// 触发可以被执行的horizon事件监听
function triggerHorizonEvents(
nativeEvtName: string,
isCapture: boolean,
nativeEvent: AnyNativeEvent,
vNode: null | VNode,
): void {
const nativeEventTarget = getEventTarget(nativeEvent);
const processingListenerList = getProcessListenersFacade(nativeEvtName, vNode, nativeEvent, nativeEventTarget, isCapture);
// 处理触发的事件队列
processListeners(processingListenerList);
}
// 其他事件正在执行中标记
let isInEventsExecution = false;
export function handleEventMain(
nativeEvtName: string,
isCapture: boolean,
nativeEvent: AnyNativeEvent,
vNode: null | VNode,
target: EventTarget,
): void {
let rootVNode = vNode;
if (vNode !== null) {
rootVNode = getExactNode(vNode, target);
if (!rootVNode) {
return;
}
}
// 有事件正在执行,同步执行事件
if (isInEventsExecution) {
return triggerHorizonEvents(nativeEvtName, isCapture, nativeEvent, rootVNode);
}
// 没有事件在执行,经过调度再执行事件
isInEventsExecution = true;
try {
return asyncUpdates(() =>
triggerHorizonEvents(
nativeEvtName,
isCapture,
nativeEvent,
rootVNode,
));
} finally {
isInEventsExecution = false;
if (shouldUpdateValue()) {
runDiscreteUpdates();
updateControlledValue();
}
}
}

View File

@ -1,108 +0,0 @@
import {VNode} from '../renderer/Types';
import {DomComponent} from '../renderer/vnode/VNodeTags';
import {throwIfTrue} from '../renderer/utils/throwIfTrue';
import type {Props} from '../dom/DOMOperator';
import {EVENT_TYPE_ALL, EVENT_TYPE_CAPTURE, EVENT_TYPE_BUBBLE} from './const';
import {ProcessingListenerList, ListenerUnitList} from './Types';
import {CustomBaseEvent} from './customEvents/CustomBaseEvent';
// 返回是否应该阻止事件响应标记disabled组件不响应鼠标事件
function shouldPrevent(
name: string,
type: string,
props: Props,
): boolean {
const canPreventMouseEvents = [
'onClick',
'onClickCapture',
'onDoubleClick',
'onDoubleClickCapture',
'onMouseDown',
'onMouseDownCapture',
'onMouseMove',
'onMouseMoveCapture',
'onMouseUp',
'onMouseUpCapture',
'onMouseEnter',
];
const interActiveElements = ['button', 'input', 'select', 'textarea'];
if (canPreventMouseEvents.includes(name)) {
return !!(props.disabled && interActiveElements.includes(type));
}
return false;
}
// 从vnode属性中获取事件listener
function getListener(
vNode: VNode,
eventName: string,
): Function | null {
const realNode = vNode.realNode;
if (realNode === null) {
return null;
}
const props = vNode.props;
if (props === null) {
return null;
}
const listener = props[eventName];
if (shouldPrevent(eventName, vNode.type, props)) {
return null;
}
throwIfTrue(
listener && typeof listener !== 'function',
'`%s` listener should be a function.',
eventName
);
return listener;
}
// 获取监听事件
export function getListenersFromTree(
targetVNode: VNode | null,
name: string | null,
horizonEvent: CustomBaseEvent,
eventType: string,
): ProcessingListenerList {
if (!name) {
return [];
}
const captureName = name + EVENT_TYPE_CAPTURE;
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 captureListener = getListener(vNode, captureName);
if (captureListener) {
listeners.unshift({
vNode,
listener: captureListener,
currentTarget: realNode,
event: horizonEvent,
});
}
}
if (eventType === EVENT_TYPE_ALL || eventType === EVENT_TYPE_BUBBLE) {
const bubbleListener = getListener(vNode, name);
if (bubbleListener) {
listeners.push({
vNode,
listener: bubbleListener,
currentTarget: realNode,
event: horizonEvent,
});
}
}
}
vNode = vNode.parent;
}
return listeners.length > 0 ? [listeners]: [];
}

View File

@ -1,55 +0,0 @@
/**
* style中的动画事件
*/
// style事件浏览器兼容前缀
const vendorPrefixes = {
animationend: {
MozAnimation: 'mozAnimationEnd',
WebkitAnimation: 'webkitAnimationEnd',
animation: 'animationend',
},
animationiteration: {
MozAnimation: 'mozAnimationIteration',
WebkitAnimation: 'webkitAnimationIteration',
animation: 'animationiteration',
},
animationstart: {
MozAnimation: 'mozAnimationStart',
WebkitAnimation: 'webkitAnimationStart',
animation: 'animationstart',
},
transitionend: {
MozTransition: 'mozTransitionEnd',
WebkitTransition: 'webkitTransitionEnd',
transition: 'transitionend',
},
};
// 获取属性中对应事件名
function getEventNameByStyle(eventName) {
const prefixMap = vendorPrefixes[eventName];
if (!prefixMap) {
return eventName;
}
const style = document.createElement('div').style
for (const styleProp in prefixMap) {
if (styleProp in style) {
return prefixMap[styleProp];
}
}
return eventName;
}
export const STYLE_AMT_END: string = getEventNameByStyle(
'animationend',
);
export const STYLE_AMT_ITERATION: string = getEventNameByStyle(
'animationiteration',
);
export const STYLE_AMT_START: string = getEventNameByStyle(
'animationstart',
);
export const STYLE_TRANS_END: string = getEventNameByStyle(
'transitionend',
);

View File

@ -1,16 +0,0 @@
import type {VNode} from '../renderer/Types';
import {CustomBaseEvent} from './customEvents/CustomBaseEvent';
export type AnyNativeEvent = KeyboardEvent | MouseEvent | TouchEvent | UIEvent | Event;
export type ListenerUnit = {
vNode: null | VNode,
listener: Function,
currentTarget: EventTarget,
event: CustomBaseEvent,
};
export type ListenerUnitList = Array<ListenerUnit>;
export type ProcessingListenerList = Array<ListenerUnitList>;

View File

@ -1,41 +0,0 @@
import {isMounted} from '../renderer/vnode/VNodeUtils';
import {SuspenseComponent} from '../renderer/vnode/VNodeTags';
import {getNearestVNode} from '../dom/DOMInternalKeys';
import {handleEventMain} from './HorizonEventMain';
import {runDiscreteUpdates} from '../renderer/Renderer';
import {getEventTarget} from './utils';
// 生成委托事件的监听方法
export function createCustomEventListener(
target: EventTarget,
nativeEvtName: string,
isCapture: boolean,
): EventListener {
return triggerDelegatedEvent.bind(null, nativeEvtName, isCapture, target);
}
// 触发委托事件
function triggerDelegatedEvent(
nativeEvtName: string,
isCapture: boolean,
targetDom: EventTarget,
nativeEvent,
) {
// 执行之前的调度事件
runDiscreteUpdates();
const nativeEventTarget = getEventTarget(nativeEvent);
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);
}

View File

@ -1,56 +0,0 @@
import {isText} from '../dom/utils/Common';
import { CHAR_CODE_ENTER, CHAR_CODE_SPACE } from './const';
export function uniqueCharCode(nativeEvent): number {
let charCode = nativeEvent.charCode;
// 火狐浏览器没有设置enter键的charCode用keyCode
if (charCode === 0 && nativeEvent.keyCode === CHAR_CODE_ENTER) {
charCode = CHAR_CODE_ENTER;
}
// 当ctrl按下时10表示enter键按下
if (charCode === 10) {
charCode = CHAR_CODE_ENTER;
}
// 忽略非打印的Enter键
if (charCode >= CHAR_CODE_SPACE || charCode === CHAR_CODE_ENTER) {
return charCode;
}
return 0;
}
// 获取事件的target对象
export function getEventTarget(nativeEvent) {
const target = nativeEvent.target || nativeEvent.srcElement || window;
if (isText(target)) {
return target.parentNode;
}
return target;
}
// 支持的输入框类型
const supportedInputTypes = ['color', 'date', 'datetime', 'datetime-local', 'email', 'month',
'number', 'password', 'range', 'search', 'tel', 'text', 'time', 'url', 'week'];
export function isTextInputElement(dom?: HTMLElement): boolean {
if (dom instanceof HTMLInputElement) {
return supportedInputTypes.includes(dom.type);
}
const nodeName = dom && dom.nodeName && dom.nodeName.toLowerCase();
return nodeName === 'textarea';
}
// 例dragEnd -> onDragEnd
export function getCustomEventNameWithOn(name) {
if (!name) {
return '';
}
const capitalizedEvent = name[0].toUpperCase() + name.slice(1);
const horizonEventName = 'on' + capitalizedEvent;
return horizonEventName;
}