!91 解决portal事件无法冒泡的问题

Merge pull request !91 from chenyuan/master
This commit is contained in:
openInula-robot 2023-12-01 03:39:19 +00:00 committed by Gitee
commit c99209f18a
No known key found for this signature in database
GPG Key ID: 173E9B9CA92EEF8F
5 changed files with 117 additions and 3 deletions

View File

@ -286,4 +286,98 @@ describe('PortalComponent Test', () => {
dispatchChangeEvent(inputRef.current, 'test');
expect(fn).toHaveBeenCalledTimes(1);
});
it('portal场景下portal下元素点击事件冒泡到父元素', () => {
class Dialog extends Inula.Component {
node;
constructor(props) {
super(props);
this.node = window.document.createElement('div');
window.document.body.appendChild(this.node);
}
render() {
return Inula.createPortal(this.props.children, this.node);
}
}
const fn = jest.fn();
const subRef = Inula.createRef();
function App() {
return (
<div onClick={fn}>
<Dialog>
<div ref={subRef}/>
</Dialog>
</div>
);
}
Inula.render(<App />, container);
Inula.act(() => {
subRef.current.dispatchEvent(new Event('click', { bubbles: true }));
});
expect(fn).toHaveBeenCalledTimes(1);
});
it('portal嵌套场景下事件委托', () => {
class Dialog extends Inula.Component {
node;
constructor(props) {
super(props);
this.node = window.document.createElement('div');
window.document.body.appendChild(this.node);
}
render() {
return Inula.createPortal(this.props.children, this.node);
}
}
const fn = jest.fn();
const inputRef = Inula.createRef();
let value = '';
const onChange = (evt) => {
value = evt.target.value;
}
let showSubPortal = () => {};
function App() {
return (
<div>
<Dialog>
<input onChange={fn}></input>
<Sub />
</Dialog>
</div>
);
}
function Sub() {
const [show, setShow] = Inula.useState(false);
showSubPortal = setShow;
return (
<div>
{
show &&
<Dialog>
<input ref={inputRef} onChange={onChange}></input>
</Dialog>
}
</div>
)
}
Inula.render(<App />, container);
Inula.act(() => {
showSubPortal(true);
});
dispatchChangeEvent(inputRef.current, 'test');
expect(value).toEqual('test');
});
});

View File

@ -16,7 +16,7 @@
/**
*
*/
import { allDelegatedInulaEvents, simulatedDelegatedEvents } from './EventHub';
import { allDelegatedInulaEvents, portalDefaultDelegatedEvents, simulatedDelegatedEvents } from './EventHub';
import { isDocument } from '../dom/utils/Common';
import { getNearestVNode, getNonDelegatedListenerMap } from '../dom/DOMInternalKeys';
import { asyncUpdates, runDiscreteUpdates } from '../renderer/TreeBuilder';
@ -89,6 +89,13 @@ export function listenSimulatedDelegatedEvents(root: VNode) {
}
}
// portal绑定默认事件
export function listenPortalEvents(root: VNode) {
for (let i = 0; i < portalDefaultDelegatedEvents.length; i++) {
lazyDelegateOnRoot(root, portalDefaultDelegatedEvents[i]);
}
}
// 通过inula事件名获取到native事件名
function getNativeEvtName(inulaEventName, capture) {
let nativeName;

View File

@ -16,6 +16,13 @@
// 需要委托的inula事件和原生事件对应关系
export const allDelegatedInulaEvents = new Map();
/**
* Portal根节点默认绑定事件parent vnode的问题
* parent vNode节点绑定了mousedown事件portal节点mousedown事件
* portal下子元素mousedown事件无法冒泡到parentNode
*/
export const portalDefaultDelegatedEvents = ['onMouseDown', 'onMouseUp', 'onKeyDown', 'onKeyUp', 'onFocus', 'onBlur', 'onClick'];
// 模拟委托事件,不冒泡事件需要利用其他事件来触发冒泡过程
export const simulatedDelegatedEvents = ['onMouseEnter', 'onMouseLeave'];
// 所有委托的原生事件集合

View File

@ -244,17 +244,22 @@ export function calcStartUpdateVNode(treeRoot: VNode) {
// 在局部更新时从上到下恢复父节点的context和PortalStack
function recoverTreeContext(vNode: VNode) {
const contextProviders: VNode[] = [];
const portalRoots: VNode[] = [];
let parent = vNode.parent;
while (parent !== null) {
if (parent.tag === ContextProvider) {
contextProviders.unshift(parent);
} else if (parent.tag === DomPortal) {
portalRoots.unshift(parent);
}
if (parent.tag === DomPortal) {
pushCurrentRoot(parent);
}
parent = parent.parent;
}
portalRoots.forEach(node => {
pushCurrentRoot(node);
});
contextProviders.forEach(node => {
setContext(node, node.props.value);
});

View File

@ -17,11 +17,12 @@ import type { VNode } from '../Types';
import { resetNamespaceCtx, setNamespaceCtx } from '../ContextSaver';
import { createChildrenByDiff } from '../diff/nodeDiffComparator';
import { popCurrentRoot, pushCurrentRoot } from '../RootStack';
import { listenSimulatedDelegatedEvents } from '../../event/EventBinding';
import { listenPortalEvents, listenSimulatedDelegatedEvents } from '../../event/EventBinding';
export function bubbleRender(processing: VNode) {
resetNamespaceCtx(processing);
listenSimulatedDelegatedEvents(processing);
listenPortalEvents(processing);
popCurrentRoot();
}