Match-id-9a5a55ba654db4419702e54249dd665a02d65553
This commit is contained in:
parent
0677dfcaca
commit
2feaad7690
|
@ -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