Skip to content

Commit

Permalink
Event delegation for NoteEventPlugin (#4424)
Browse files Browse the repository at this point in the history
  • Loading branch information
fantactuka committed May 6, 2023
1 parent fe940b9 commit 639d636
Showing 1 changed file with 33 additions and 59 deletions.
92 changes: 33 additions & 59 deletions packages/lexical-react/src/LexicalNodeEventPlugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,14 @@
*
*/

import type {Klass, LexicalEditor, LexicalNode, NodeKey} from 'lexical';

import {useLexicalComposerContext} from '@lexical/react/LexicalComposerContext';
import {
type Klass,
type LexicalEditor,
type LexicalNode,
type NodeKey,
$nodesOfType,
} from 'lexical';
import {useRef} from 'react';
import useLayoutEffect from 'shared/useLayoutEffect';
import {$findMatchingParent} from '@lexical/utils';
import {$getNearestNodeFromDOMNode} from 'lexical';
import {useEffect, useRef} from 'react';

const capturedEvents = new Set<string>(['mouseenter', 'mouseleave']);

export function NodeEventPlugin({
nodeType,
Expand All @@ -35,62 +33,38 @@ export function NodeEventPlugin({

listenerRef.current = eventListener;

useLayoutEffect(() => {
const registeredElements: WeakSet<HTMLElement> = new WeakSet();
const listeners: Map<HTMLElement, (event: Event) => void> = new Map();

const addElementListener = (element: HTMLElement, key: NodeKey) => {
registeredElements.add(element);

const listener = (event: Event) => {
listenerRef.current(event, editor, key);
};
useEffect(() => {
const isCaptured = capturedEvents.has(eventType);

element.addEventListener(eventType, listener);
listeners.set(element, listener);
};

editor.getEditorState().read(() => {
for (const node of $nodesOfType(nodeType)) {
const key = node.getKey();
const element = editor.getElementByKey(key);

if (element === null || registeredElements.has(element)) {
continue;
const onEvent = (event: Event) => {
editor.update(() => {
const nearestNode = $getNearestNodeFromDOMNode(event.target as Element);
if (nearestNode !== null) {
const targetNode = isCaptured
? nearestNode instanceof nodeType
? nearestNode
: null
: $findMatchingParent(
nearestNode,
(node) => node instanceof nodeType,
);
if (targetNode !== null) {
listenerRef.current(event, editor, targetNode.getKey());
return;
}
}
});
};

addElementListener(element, key);
return editor.registerRootListener((rootElement, prevRootElement) => {
if (rootElement) {
rootElement.addEventListener(eventType, onEvent, isCaptured);
}
});

const removeMutationListener = editor.registerMutationListener(
nodeType,
(mutations) => {
editor.getEditorState().read(() => {
for (const [key, mutation] of mutations) {
const element: null | HTMLElement = editor.getElementByKey(key);

if (
// "updated" might represent a moved node, in which case a new DOM
// element is created. That requires us to add an event listener too.
(mutation === 'created' || mutation === 'updated') &&
element !== null &&
!registeredElements.has(element)
) {
addElementListener(element, key);
}
}
});
},
);

return () => {
for (const [element, listener] of listeners) {
element.removeEventListener(eventType, listener);
if (prevRootElement) {
prevRootElement.removeEventListener(eventType, onEvent, isCaptured);
}

removeMutationListener();
};
});
// We intentionally don't respect changes to eventType.
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [editor, nodeType]);
Expand Down

2 comments on commit 639d636

@vercel
Copy link

@vercel vercel bot commented on 639d636 May 6, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Successfully deployed to the following URLs:

lexical – ./packages/lexical-website

lexical-fbopensource.vercel.app
lexical-git-main-fbopensource.vercel.app
lexical.dev
www.lexical.dev
lexicaljs.org
lexicaljs.com

@vercel
Copy link

@vercel vercel bot commented on 639d636 May 6, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Successfully deployed to the following URLs:

lexical-playground – ./packages/lexical-playground

lexical-playground-fbopensource.vercel.app
lexical-playground-git-main-fbopensource.vercel.app
lexical-playground.vercel.app
playground.lexical.dev

Please sign in to comment.