@lexical/react 0.12.2 → 0.12.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LexicalAutoEmbedPlugin.d.ts +3 -2
- package/LexicalAutoEmbedPlugin.dev.js +4 -14
- package/LexicalAutoEmbedPlugin.prod.js +4 -3
- package/LexicalAutoFocusPlugin.dev.js +0 -1
- package/LexicalAutoLinkPlugin.dev.js +126 -73
- package/LexicalAutoLinkPlugin.prod.js +11 -7
- package/LexicalBlockWithAlignableContents.dev.js +0 -10
- package/LexicalCharacterLimitPlugin.dev.js +7 -46
- package/LexicalCheckListPlugin.dev.js +10 -48
- package/LexicalClearEditorPlugin.dev.js +1 -1
- package/LexicalClickableLinkPlugin.dev.js +2 -20
- package/LexicalCollaborationContext.dev.js +0 -3
- package/LexicalCollaborationPlugin.dev.js +8 -37
- package/LexicalComposer.d.ts +3 -7
- package/LexicalComposer.dev.js +9 -11
- package/LexicalComposer.js.flow +4 -4
- package/LexicalComposer.prod.js +2 -1
- package/LexicalComposerContext.dev.js +0 -6
- package/LexicalContentEditable.dev.js +1 -2
- package/LexicalContextMenuPlugin.d.ts +3 -2
- package/LexicalContextMenuPlugin.dev.js +28 -82
- package/LexicalContextMenuPlugin.prod.js +16 -15
- package/LexicalDecoratorBlockNode.dev.js +0 -6
- package/LexicalEditorRefPlugin.dev.js +0 -3
- package/LexicalHashtagPlugin.dev.js +73 -43
- package/LexicalHorizontalRuleNode.dev.js +0 -21
- package/LexicalHorizontalRulePlugin.dev.js +0 -4
- package/LexicalLinkPlugin.dev.js +4 -10
- package/LexicalListPlugin.dev.js +0 -2
- package/LexicalMarkdownShortcutPlugin.dev.js +2 -2
- package/LexicalNestedComposer.d.ts +2 -2
- package/LexicalNestedComposer.dev.js +18 -16
- package/LexicalNestedComposer.js.flow +7 -2
- package/LexicalNestedComposer.prod.js +4 -3
- package/LexicalNodeEventPlugin.dev.js +2 -6
- package/LexicalNodeMenuPlugin.d.ts +3 -2
- package/LexicalNodeMenuPlugin.dev.js +27 -83
- package/LexicalNodeMenuPlugin.prod.js +15 -15
- package/LexicalOnChangePlugin.dev.js +1 -1
- package/LexicalPlainTextPlugin.dev.js +8 -12
- package/LexicalRichTextPlugin.dev.js +8 -12
- package/LexicalTabIndentationPlugin.dev.js +7 -16
- package/LexicalTableOfContents.dev.js +5 -33
- package/LexicalTablePlugin.dev.js +11 -28
- package/LexicalTreeView.dev.js +14 -79
- package/LexicalTypeaheadMenuPlugin.d.ts +4 -3
- package/LexicalTypeaheadMenuPlugin.dev.js +39 -176
- package/LexicalTypeaheadMenuPlugin.prod.js +19 -20
- package/package.json +19 -19
- package/shared/LexicalMenu.d.ts +3 -2
- package/useLexicalEditable.dev.js +1 -5
- package/useLexicalIsTextContentEmpty.dev.js +1 -0
- package/useLexicalNodeSelection.dev.js +0 -7
- package/useLexicalSubscription.dev.js +1 -3
|
@@ -22,6 +22,7 @@ var richText = require('@lexical/rich-text');
|
|
|
22
22
|
* LICENSE file in the root directory of this source tree.
|
|
23
23
|
*
|
|
24
24
|
*/
|
|
25
|
+
|
|
25
26
|
const CAN_USE_DOM = typeof window !== 'undefined' && typeof window.document !== 'undefined' && typeof window.document.createElement !== 'undefined';
|
|
26
27
|
|
|
27
28
|
/**
|
|
@@ -41,12 +42,10 @@ var useLayoutEffect = useLayoutEffectImpl;
|
|
|
41
42
|
* LICENSE file in the root directory of this source tree.
|
|
42
43
|
*
|
|
43
44
|
*/
|
|
44
|
-
|
|
45
45
|
function canShowPlaceholderFromCurrentEditorState(editor) {
|
|
46
46
|
const currentCanShowPlaceholder = editor.getEditorState().read(text.$canShowPlaceholderCurry(editor.isComposing()));
|
|
47
47
|
return currentCanShowPlaceholder;
|
|
48
48
|
}
|
|
49
|
-
|
|
50
49
|
function useCanShowPlaceholder(editor) {
|
|
51
50
|
const [canShowPlaceholder, setCanShowPlaceholder] = React.useState(() => canShowPlaceholderFromCurrentEditorState(editor));
|
|
52
51
|
useLayoutEffect(() => {
|
|
@@ -54,7 +53,6 @@ function useCanShowPlaceholder(editor) {
|
|
|
54
53
|
const currentCanShowPlaceholder = canShowPlaceholderFromCurrentEditorState(editor);
|
|
55
54
|
setCanShowPlaceholder(currentCanShowPlaceholder);
|
|
56
55
|
}
|
|
57
|
-
|
|
58
56
|
resetCanShowPlaceholder();
|
|
59
57
|
return utils.mergeRegister(editor.registerUpdateListener(() => {
|
|
60
58
|
resetCanShowPlaceholder();
|
|
@@ -73,8 +71,9 @@ function useCanShowPlaceholder(editor) {
|
|
|
73
71
|
*
|
|
74
72
|
*/
|
|
75
73
|
function useDecorators(editor, ErrorBoundary) {
|
|
76
|
-
const [decorators, setDecorators] = React.useState(() => editor.getDecorators());
|
|
74
|
+
const [decorators, setDecorators] = React.useState(() => editor.getDecorators());
|
|
77
75
|
|
|
76
|
+
// Subscribe to changes
|
|
78
77
|
useLayoutEffect(() => {
|
|
79
78
|
return editor.registerDecoratorListener(nextDecorators => {
|
|
80
79
|
reactDom.flushSync(() => {
|
|
@@ -87,12 +86,12 @@ function useDecorators(editor, ErrorBoundary) {
|
|
|
87
86
|
// nothing will be rendered on initial pass. We can get around that by
|
|
88
87
|
// ensuring that we set the value.
|
|
89
88
|
setDecorators(editor.getDecorators());
|
|
90
|
-
}, [editor]);
|
|
89
|
+
}, [editor]);
|
|
91
90
|
|
|
91
|
+
// Return decorators defined as React Portals
|
|
92
92
|
return React.useMemo(() => {
|
|
93
93
|
const decoratedPortals = [];
|
|
94
94
|
const decoratorKeys = Object.keys(decorators);
|
|
95
|
-
|
|
96
95
|
for (let i = 0; i < decoratorKeys.length; i++) {
|
|
97
96
|
const nodeKey = decoratorKeys[i];
|
|
98
97
|
const reactDecorator = /*#__PURE__*/React.createElement(ErrorBoundary, {
|
|
@@ -101,12 +100,10 @@ function useDecorators(editor, ErrorBoundary) {
|
|
|
101
100
|
fallback: null
|
|
102
101
|
}, decorators[nodeKey]));
|
|
103
102
|
const element = editor.getElementByKey(nodeKey);
|
|
104
|
-
|
|
105
103
|
if (element !== null) {
|
|
106
104
|
decoratedPortals.push( /*#__PURE__*/reactDom.createPortal(reactDecorator, element, nodeKey));
|
|
107
105
|
}
|
|
108
106
|
}
|
|
109
|
-
|
|
110
107
|
return decoratedPortals;
|
|
111
108
|
}, [ErrorBoundary, decorators, editor]);
|
|
112
109
|
}
|
|
@@ -120,7 +117,9 @@ function useDecorators(editor, ErrorBoundary) {
|
|
|
120
117
|
*/
|
|
121
118
|
function useRichTextSetup(editor) {
|
|
122
119
|
useLayoutEffect(() => {
|
|
123
|
-
return utils.mergeRegister(richText.registerRichText(editor), dragon.registerDragonSupport(editor));
|
|
120
|
+
return utils.mergeRegister(richText.registerRichText(editor), dragon.registerDragonSupport(editor));
|
|
121
|
+
|
|
122
|
+
// We only do this for init
|
|
124
123
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
125
124
|
}, [editor]);
|
|
126
125
|
}
|
|
@@ -144,18 +143,15 @@ function RichTextPlugin({
|
|
|
144
143
|
content: placeholder
|
|
145
144
|
}), decorators);
|
|
146
145
|
}
|
|
147
|
-
|
|
148
146
|
function Placeholder({
|
|
149
147
|
content
|
|
150
148
|
}) {
|
|
151
149
|
const [editor] = LexicalComposerContext.useLexicalComposerContext();
|
|
152
150
|
const showPlaceholder = useCanShowPlaceholder(editor);
|
|
153
151
|
const editable = useLexicalEditable();
|
|
154
|
-
|
|
155
152
|
if (!showPlaceholder) {
|
|
156
153
|
return null;
|
|
157
154
|
}
|
|
158
|
-
|
|
159
155
|
if (typeof content === 'function') {
|
|
160
156
|
return content(editable);
|
|
161
157
|
} else {
|
|
@@ -18,7 +18,6 @@ var react = require('react');
|
|
|
18
18
|
* LICENSE file in the root directory of this source tree.
|
|
19
19
|
*
|
|
20
20
|
*/
|
|
21
|
-
|
|
22
21
|
function indentOverTab(selection) {
|
|
23
22
|
// const handled = new Set();
|
|
24
23
|
const nodes = selection.getNodes();
|
|
@@ -26,56 +25,48 @@ function indentOverTab(selection) {
|
|
|
26
25
|
if (lexical.$isBlockElementNode(node) && node.canIndent()) {
|
|
27
26
|
return node;
|
|
28
27
|
}
|
|
29
|
-
|
|
30
28
|
return null;
|
|
31
|
-
});
|
|
32
|
-
|
|
29
|
+
});
|
|
30
|
+
// 1. If selection spans across canIndent block nodes: indent
|
|
33
31
|
if (canIndentBlockNodes.length > 0) {
|
|
34
32
|
return true;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
|
|
33
|
+
}
|
|
34
|
+
// 2. If first (anchor/focus) is at block start: indent
|
|
38
35
|
const anchor = selection.anchor;
|
|
39
36
|
const focus = selection.focus;
|
|
40
37
|
const first = focus.isBefore(anchor) ? focus : anchor;
|
|
41
38
|
const firstNode = first.getNode();
|
|
42
39
|
const firstBlock = utils.$getNearestBlockElementAncestorOrThrow(firstNode);
|
|
43
|
-
|
|
44
40
|
if (firstBlock.canIndent()) {
|
|
45
41
|
const firstBlockKey = firstBlock.getKey();
|
|
46
42
|
let selectionAtStart = lexical.$createRangeSelection();
|
|
47
43
|
selectionAtStart.anchor.set(firstBlockKey, 0, 'element');
|
|
48
44
|
selectionAtStart.focus.set(firstBlockKey, 0, 'element');
|
|
49
45
|
selectionAtStart = lexical.$normalizeSelection__EXPERIMENTAL(selectionAtStart);
|
|
50
|
-
|
|
51
46
|
if (selectionAtStart.anchor.is(first)) {
|
|
52
47
|
return true;
|
|
53
48
|
}
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
|
|
49
|
+
}
|
|
50
|
+
// 3. Else: tab
|
|
57
51
|
return false;
|
|
58
52
|
}
|
|
59
|
-
|
|
60
53
|
function registerTabIndentation(editor) {
|
|
61
54
|
return editor.registerCommand(lexical.KEY_TAB_COMMAND, event => {
|
|
62
55
|
const selection = lexical.$getSelection();
|
|
63
|
-
|
|
64
56
|
if (!lexical.$isRangeSelection(selection)) {
|
|
65
57
|
return false;
|
|
66
58
|
}
|
|
67
|
-
|
|
68
59
|
event.preventDefault();
|
|
69
60
|
const command = indentOverTab(selection) ? event.shiftKey ? lexical.OUTDENT_CONTENT_COMMAND : lexical.INDENT_CONTENT_COMMAND : lexical.INSERT_TAB_COMMAND;
|
|
70
61
|
return editor.dispatchCommand(command, undefined);
|
|
71
62
|
}, lexical.COMMAND_PRIORITY_EDITOR);
|
|
72
63
|
}
|
|
64
|
+
|
|
73
65
|
/**
|
|
74
66
|
* This plugin adds the ability to indent content using the tab key. Generally, we don't
|
|
75
67
|
* recommend using this plugin as it could negatively affect acessibility for keyboard
|
|
76
68
|
* users, causing focus to become trapped within the editor.
|
|
77
69
|
*/
|
|
78
|
-
|
|
79
70
|
function TabIndentationPlugin() {
|
|
80
71
|
const [editor] = LexicalComposerContext.useLexicalComposerContext();
|
|
81
72
|
react.useEffect(() => {
|
|
@@ -18,50 +18,39 @@ var react = require('react');
|
|
|
18
18
|
* LICENSE file in the root directory of this source tree.
|
|
19
19
|
*
|
|
20
20
|
*/
|
|
21
|
-
|
|
22
21
|
function toEntry(heading) {
|
|
23
22
|
return [heading.getKey(), heading.getTextContent(), heading.getTag()];
|
|
24
23
|
}
|
|
25
|
-
|
|
26
24
|
function $insertHeadingIntoTableOfContents(prevHeading, newHeading, currentTableOfContents) {
|
|
27
25
|
if (newHeading === null) {
|
|
28
26
|
return currentTableOfContents;
|
|
29
27
|
}
|
|
30
|
-
|
|
31
28
|
const newEntry = toEntry(newHeading);
|
|
32
29
|
let newTableOfContents = [];
|
|
33
|
-
|
|
34
30
|
if (prevHeading === null) {
|
|
35
31
|
newTableOfContents = [newEntry, ...currentTableOfContents];
|
|
36
32
|
} else {
|
|
37
33
|
for (let i = 0; i < currentTableOfContents.length; i++) {
|
|
38
34
|
const key = currentTableOfContents[i][0];
|
|
39
35
|
newTableOfContents.push(currentTableOfContents[i]);
|
|
40
|
-
|
|
41
36
|
if (key === prevHeading.getKey() && key !== newHeading.getKey()) {
|
|
42
37
|
newTableOfContents.push(newEntry);
|
|
43
38
|
}
|
|
44
39
|
}
|
|
45
40
|
}
|
|
46
|
-
|
|
47
41
|
return newTableOfContents;
|
|
48
42
|
}
|
|
49
|
-
|
|
50
43
|
function $deleteHeadingFromTableOfContents(key, currentTableOfContents) {
|
|
51
44
|
const newTableOfContents = [];
|
|
52
|
-
|
|
53
45
|
for (const heading of currentTableOfContents) {
|
|
54
46
|
if (heading[0] !== key) {
|
|
55
47
|
newTableOfContents.push(heading);
|
|
56
48
|
}
|
|
57
49
|
}
|
|
58
|
-
|
|
59
50
|
return newTableOfContents;
|
|
60
51
|
}
|
|
61
|
-
|
|
62
52
|
function $updateHeadingInTableOfContents(heading, currentTableOfContents) {
|
|
63
53
|
const newTableOfContents = [];
|
|
64
|
-
|
|
65
54
|
for (const oldHeading of currentTableOfContents) {
|
|
66
55
|
if (oldHeading[0] === heading.getKey()) {
|
|
67
56
|
newTableOfContents.push(toEntry(heading));
|
|
@@ -69,38 +58,30 @@ function $updateHeadingInTableOfContents(heading, currentTableOfContents) {
|
|
|
69
58
|
newTableOfContents.push(oldHeading);
|
|
70
59
|
}
|
|
71
60
|
}
|
|
72
|
-
|
|
73
61
|
return newTableOfContents;
|
|
74
62
|
}
|
|
63
|
+
|
|
75
64
|
/**
|
|
76
65
|
* Returns the updated table of contents, placing the given `heading` before the given `prevHeading`. If `prevHeading`
|
|
77
66
|
* is undefined, `heading` is placed at the start of table of contents
|
|
78
67
|
*/
|
|
79
|
-
|
|
80
|
-
|
|
81
68
|
function $updateHeadingPosition(prevHeading, heading, currentTableOfContents) {
|
|
82
69
|
const newTableOfContents = [];
|
|
83
70
|
const newEntry = toEntry(heading);
|
|
84
|
-
|
|
85
71
|
if (!prevHeading) {
|
|
86
72
|
newTableOfContents.push(newEntry);
|
|
87
73
|
}
|
|
88
|
-
|
|
89
74
|
for (const oldHeading of currentTableOfContents) {
|
|
90
75
|
if (oldHeading[0] === heading.getKey()) {
|
|
91
76
|
continue;
|
|
92
77
|
}
|
|
93
|
-
|
|
94
78
|
newTableOfContents.push(oldHeading);
|
|
95
|
-
|
|
96
79
|
if (prevHeading && oldHeading[0] === prevHeading.getKey()) {
|
|
97
80
|
newTableOfContents.push(newEntry);
|
|
98
81
|
}
|
|
99
82
|
}
|
|
100
|
-
|
|
101
83
|
return newTableOfContents;
|
|
102
84
|
}
|
|
103
|
-
|
|
104
85
|
function LexicalTableOfContentsPlugin({
|
|
105
86
|
children
|
|
106
87
|
}) {
|
|
@@ -112,61 +93,52 @@ function LexicalTableOfContentsPlugin({
|
|
|
112
93
|
editor.getEditorState().read(() => {
|
|
113
94
|
const root = lexical.$getRoot();
|
|
114
95
|
const rootChildren = root.getChildren();
|
|
115
|
-
|
|
116
96
|
for (const child of rootChildren) {
|
|
117
97
|
if (richText.$isHeadingNode(child)) {
|
|
118
98
|
currentTableOfContents.push([child.getKey(), child.getTextContent(), child.getTag()]);
|
|
119
99
|
}
|
|
120
100
|
}
|
|
121
|
-
|
|
122
101
|
setTableOfContents(currentTableOfContents);
|
|
123
|
-
});
|
|
102
|
+
});
|
|
124
103
|
|
|
104
|
+
// Listen to updates to heading mutations and update state
|
|
125
105
|
const removeHeaderMutationListener = editor.registerMutationListener(richText.HeadingNode, mutatedNodes => {
|
|
126
106
|
editor.getEditorState().read(() => {
|
|
127
107
|
for (const [nodeKey, mutation] of mutatedNodes) {
|
|
128
108
|
if (mutation === 'created') {
|
|
129
109
|
const newHeading = lexical.$getNodeByKey(nodeKey);
|
|
130
|
-
|
|
131
110
|
if (newHeading !== null) {
|
|
132
111
|
let prevHeading = newHeading.getPreviousSibling();
|
|
133
|
-
|
|
134
112
|
while (prevHeading !== null && !richText.$isHeadingNode(prevHeading)) {
|
|
135
113
|
prevHeading = prevHeading.getPreviousSibling();
|
|
136
114
|
}
|
|
137
|
-
|
|
138
115
|
currentTableOfContents = $insertHeadingIntoTableOfContents(prevHeading, newHeading, currentTableOfContents);
|
|
139
116
|
}
|
|
140
117
|
} else if (mutation === 'destroyed') {
|
|
141
118
|
currentTableOfContents = $deleteHeadingFromTableOfContents(nodeKey, currentTableOfContents);
|
|
142
119
|
} else if (mutation === 'updated') {
|
|
143
120
|
const newHeading = lexical.$getNodeByKey(nodeKey);
|
|
144
|
-
|
|
145
121
|
if (newHeading !== null) {
|
|
146
122
|
let prevHeading = newHeading.getPreviousSibling();
|
|
147
|
-
|
|
148
123
|
while (prevHeading !== null && !richText.$isHeadingNode(prevHeading)) {
|
|
149
124
|
prevHeading = prevHeading.getPreviousSibling();
|
|
150
125
|
}
|
|
151
|
-
|
|
152
126
|
currentTableOfContents = $updateHeadingPosition(prevHeading, newHeading, currentTableOfContents);
|
|
153
127
|
}
|
|
154
128
|
}
|
|
155
129
|
}
|
|
156
|
-
|
|
157
130
|
setTableOfContents(currentTableOfContents);
|
|
158
131
|
});
|
|
159
|
-
});
|
|
132
|
+
});
|
|
160
133
|
|
|
134
|
+
// Listen to text node mutation updates
|
|
161
135
|
const removeTextNodeMutationListener = editor.registerMutationListener(lexical.TextNode, mutatedNodes => {
|
|
162
136
|
editor.getEditorState().read(() => {
|
|
163
137
|
for (const [nodeKey, mutation] of mutatedNodes) {
|
|
164
138
|
if (mutation === 'updated') {
|
|
165
139
|
const currNode = lexical.$getNodeByKey(nodeKey);
|
|
166
|
-
|
|
167
140
|
if (currNode !== null) {
|
|
168
141
|
const parentNode = currNode.getParentOrThrow();
|
|
169
|
-
|
|
170
142
|
if (richText.$isHeadingNode(parentNode)) {
|
|
171
143
|
currentTableOfContents = $updateHeadingInTableOfContents(parentNode, currentTableOfContents);
|
|
172
144
|
setTableOfContents(currentTableOfContents);
|
|
@@ -31,7 +31,6 @@ function TablePlugin({
|
|
|
31
31
|
throw Error(`TablePlugin: TableNode, TableCellNode or TableRowNode not registered on editor`);
|
|
32
32
|
}
|
|
33
33
|
}
|
|
34
|
-
|
|
35
34
|
return editor.registerCommand(table.INSERT_TABLE_COMMAND, ({
|
|
36
35
|
columns,
|
|
37
36
|
rows,
|
|
@@ -40,32 +39,27 @@ function TablePlugin({
|
|
|
40
39
|
const tableNode = table.$createTableNodeWithDimensions(Number(rows), Number(columns), includeHeaders);
|
|
41
40
|
utils.$insertNodeToNearestRoot(tableNode);
|
|
42
41
|
const firstDescendant = tableNode.getFirstDescendant();
|
|
43
|
-
|
|
44
42
|
if (lexical.$isTextNode(firstDescendant)) {
|
|
45
43
|
firstDescendant.select();
|
|
46
44
|
}
|
|
47
|
-
|
|
48
45
|
return true;
|
|
49
46
|
}, lexical.COMMAND_PRIORITY_EDITOR);
|
|
50
47
|
}, [editor]);
|
|
51
48
|
react.useEffect(() => {
|
|
52
49
|
const tableSelections = new Map();
|
|
53
|
-
|
|
54
50
|
const initializeTableNode = tableNode => {
|
|
55
51
|
const nodeKey = tableNode.getKey();
|
|
56
52
|
const tableElement = editor.getElementByKey(nodeKey);
|
|
57
|
-
|
|
58
53
|
if (tableElement && !tableSelections.has(nodeKey)) {
|
|
59
54
|
const tableSelection = table.applyTableHandlers(tableNode, tableElement, editor, hasTabHandler);
|
|
60
55
|
tableSelections.set(nodeKey, tableSelection);
|
|
61
56
|
}
|
|
62
|
-
};
|
|
63
|
-
// won't be initialized from mutation[create] listener. Instead doing it here,
|
|
64
|
-
|
|
57
|
+
};
|
|
65
58
|
|
|
59
|
+
// Plugins might be loaded _after_ initial content is set, hence existing table nodes
|
|
60
|
+
// won't be initialized from mutation[create] listener. Instead doing it here,
|
|
66
61
|
editor.getEditorState().read(() => {
|
|
67
62
|
const tableNodes = lexical.$nodesOfType(table.TableNode);
|
|
68
|
-
|
|
69
63
|
for (const tableNode of tableNodes) {
|
|
70
64
|
if (table.$isTableNode(tableNode)) {
|
|
71
65
|
initializeTableNode(tableNode);
|
|
@@ -77,14 +71,12 @@ function TablePlugin({
|
|
|
77
71
|
if (mutation === 'created') {
|
|
78
72
|
editor.getEditorState().read(() => {
|
|
79
73
|
const tableNode = lexical.$getNodeByKey(nodeKey);
|
|
80
|
-
|
|
81
74
|
if (table.$isTableNode(tableNode)) {
|
|
82
75
|
initializeTableNode(tableNode);
|
|
83
76
|
}
|
|
84
77
|
});
|
|
85
78
|
} else if (mutation === 'destroyed') {
|
|
86
79
|
const tableSelection = tableSelections.get(nodeKey);
|
|
87
|
-
|
|
88
80
|
if (tableSelection !== undefined) {
|
|
89
81
|
tableSelection.removeListeners();
|
|
90
82
|
tableSelections.delete(nodeKey);
|
|
@@ -93,58 +85,50 @@ function TablePlugin({
|
|
|
93
85
|
}
|
|
94
86
|
});
|
|
95
87
|
return () => {
|
|
96
|
-
unregisterMutationListener();
|
|
88
|
+
unregisterMutationListener();
|
|
89
|
+
// Hook might be called multiple times so cleaning up tables listeners as well,
|
|
97
90
|
// as it'll be reinitialized during recurring call
|
|
98
|
-
|
|
99
91
|
for (const [, tableSelection] of tableSelections) {
|
|
100
92
|
tableSelection.removeListeners();
|
|
101
93
|
}
|
|
102
94
|
};
|
|
103
|
-
}, [editor, hasTabHandler]);
|
|
95
|
+
}, [editor, hasTabHandler]);
|
|
104
96
|
|
|
97
|
+
// Unmerge cells when the feature isn't enabled
|
|
105
98
|
react.useEffect(() => {
|
|
106
99
|
if (hasCellMerge) {
|
|
107
100
|
return;
|
|
108
101
|
}
|
|
109
|
-
|
|
110
102
|
return editor.registerNodeTransform(table.TableCellNode, node => {
|
|
111
103
|
if (node.getColSpan() > 1 || node.getRowSpan() > 1) {
|
|
112
104
|
// When we have rowSpan we have to map the entire Table to understand where the new Cells
|
|
113
105
|
// fit best; let's analyze all Cells at once to save us from further transform iterations
|
|
114
106
|
const [,, gridNode] = lexical.DEPRECATED_$getNodeTriplet(node);
|
|
115
|
-
const [gridMap] = lexical.DEPRECATED_$computeGridMap(gridNode, node, node);
|
|
116
|
-
|
|
107
|
+
const [gridMap] = lexical.DEPRECATED_$computeGridMap(gridNode, node, node);
|
|
108
|
+
// TODO this function expects Tables to be normalized. Look into this once it exists
|
|
117
109
|
const rowsCount = gridMap.length;
|
|
118
110
|
const columnsCount = gridMap[0].length;
|
|
119
111
|
let row = gridNode.getFirstChild();
|
|
120
|
-
|
|
121
112
|
if (!lexical.DEPRECATED_$isGridRowNode(row)) {
|
|
122
113
|
throw Error(`Expected TableNode first child to be a RowNode`);
|
|
123
114
|
}
|
|
124
|
-
|
|
125
115
|
const unmerged = [];
|
|
126
|
-
|
|
127
116
|
for (let i = 0; i < rowsCount; i++) {
|
|
128
117
|
if (i !== 0) {
|
|
129
118
|
row = row.getNextSibling();
|
|
130
|
-
|
|
131
119
|
if (!lexical.DEPRECATED_$isGridRowNode(row)) {
|
|
132
120
|
throw Error(`Expected TableNode first child to be a RowNode`);
|
|
133
121
|
}
|
|
134
122
|
}
|
|
135
|
-
|
|
136
123
|
let lastRowCell = null;
|
|
137
|
-
|
|
138
124
|
for (let j = 0; j < columnsCount; j++) {
|
|
139
125
|
const cellMap = gridMap[i][j];
|
|
140
126
|
const cell = cellMap.cell;
|
|
141
|
-
|
|
142
127
|
if (cellMap.startRow === i && cellMap.startColumn === j) {
|
|
143
128
|
lastRowCell = cell;
|
|
144
129
|
unmerged.push(cell);
|
|
145
130
|
} else if (cell.getColSpan() > 1 || cell.getRowSpan() > 1) {
|
|
146
131
|
const newCell = table.$createTableCellNode(cell.__headerState);
|
|
147
|
-
|
|
148
132
|
if (lastRowCell !== null) {
|
|
149
133
|
lastRowCell.insertAfter(newCell);
|
|
150
134
|
} else {
|
|
@@ -153,20 +137,19 @@ function TablePlugin({
|
|
|
153
137
|
}
|
|
154
138
|
}
|
|
155
139
|
}
|
|
156
|
-
|
|
157
140
|
for (const cell of unmerged) {
|
|
158
141
|
cell.setColSpan(1);
|
|
159
142
|
cell.setRowSpan(1);
|
|
160
143
|
}
|
|
161
144
|
}
|
|
162
145
|
});
|
|
163
|
-
}, [editor, hasCellMerge]);
|
|
146
|
+
}, [editor, hasCellMerge]);
|
|
164
147
|
|
|
148
|
+
// Remove cell background color when feature is disabled
|
|
165
149
|
react.useEffect(() => {
|
|
166
150
|
if (hasCellBackgroundColor) {
|
|
167
151
|
return;
|
|
168
152
|
}
|
|
169
|
-
|
|
170
153
|
return editor.registerNodeTransform(table.TableCellNode, node => {
|
|
171
154
|
if (node.getBackgroundColor() !== null) {
|
|
172
155
|
node.setBackgroundColor(null);
|