@blocklet/editor 2.2.47 → 2.3.1
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/lib/ext/PostLinkEmbedPlugin/PostLinkNode.d.ts +2 -2
- package/lib/ext/PostLinkEmbedPlugin/PostLinkNode.js +3 -3
- package/lib/main/editor.js +3 -7
- package/lib/main/index.css +16 -18
- package/lib/main/markdown-editor/editor.js +1 -1
- package/lib/main/markdown-editor/transformers.js +7 -6
- package/lib/main/nodes/EmojiNode.d.ts +1 -2
- package/lib/main/nodes/EmojiNode.js +4 -9
- package/lib/main/nodes/EquationNode.js +14 -7
- package/lib/main/nodes/ImageComponent.js +2 -2
- package/lib/main/nodes/PlaygroundNodes.js +0 -2
- package/lib/main/nodes/StickyComponent.js +1 -1
- package/lib/main/plugins/CodeActionMenuPlugin/index.js +24 -28
- package/lib/main/plugins/MarkdownTransformers/index.d.ts +1 -4
- package/lib/main/plugins/MarkdownTransformers/index.js +48 -79
- package/lib/main/plugins/TableActionMenuPlugin/index.js +229 -171
- package/lib/main/plugins/TableCellResizer/index.css +8 -2
- package/lib/main/plugins/TableCellResizer/index.js +168 -120
- package/lib/main/plugins/TableOfContentsPlugin/index.js +2 -2
- package/lib/main/plugins/TablePlugin.d.ts +3 -4
- package/lib/main/plugins/TablePlugin.js +7 -24
- package/lib/main/plugins/ToolbarPlugin/index.js +0 -1
- package/lib/main/themes/defaultTheme.js +3 -1
- package/lib/main/ui/ContentEditable.d.ts +1 -4
- package/lib/main/ui/Modal.css +1 -1
- package/lib/main/ui/TextInput.d.ts +4 -3
- package/lib/main/ui/TextInput.js +3 -19
- package/package.json +23 -24
- package/lib/main/nodes/AutocompleteNode.d.ts +0 -34
- package/lib/main/nodes/AutocompleteNode.js +0 -52
- package/lib/main/nodes/EquationComponent.d.ts +0 -16
- package/lib/main/nodes/EquationComponent.js +0 -64
- package/lib/main/plugins/ActionsPlugin/index.d.ts +0 -11
- package/lib/main/plugins/ActionsPlugin/index.js +0 -129
- package/lib/main/plugins/AutocompletePlugin/index.d.ts +0 -10
- package/lib/main/plugins/AutocompletePlugin/index.js +0 -2461
- package/lib/main/plugins/CodeActionMenuPlugin/components/PrettierButton/index.css +0 -14
- package/lib/main/plugins/CodeActionMenuPlugin/components/PrettierButton/index.d.ts +0 -17
- package/lib/main/plugins/CodeActionMenuPlugin/components/PrettierButton/index.js +0 -95
- package/lib/main/plugins/EquationsPlugin/index.d.ts +0 -21
- package/lib/main/plugins/EquationsPlugin/index.js +0 -42
- package/lib/main/plugins/SpeechToTextPlugin/index.d.ts +0 -12
- package/lib/main/plugins/SpeechToTextPlugin/index.js +0 -87
- package/lib/main/ui/EquationEditor.css +0 -38
- package/lib/main/ui/EquationEditor.d.ts +0 -19
- package/lib/main/ui/EquationEditor.js +0 -26
- package/lib/main/ui/KatexEquationAlterer.css +0 -41
- package/lib/main/ui/KatexEquationAlterer.d.ts +0 -15
- package/lib/main/ui/KatexEquationAlterer.js +0 -34
- package/lib/main/ui/KatexRenderer.d.ts +0 -13
- package/lib/main/ui/KatexRenderer.js +0 -33
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
2
|
import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext';
|
|
3
|
-
import useLexicalEditable from '@lexical/react/useLexicalEditable';
|
|
4
|
-
import { $
|
|
5
|
-
import {
|
|
3
|
+
import { useLexicalEditable } from '@lexical/react/useLexicalEditable';
|
|
4
|
+
import { $computeTableMapSkipCellCheck, $deleteTableColumnAtSelection, $deleteTableRowAtSelection, $getNodeTriplet, $getTableCellNodeFromLexicalNode, $getTableColumnIndexFromTableCellNode, $getTableNodeFromLexicalNodeOrThrow, $getTableRowIndexFromTableCellNode, $insertTableColumnAtSelection, $insertTableRowAtSelection, $isTableCellNode, $isTableSelection, $mergeCells, $unmergeCell, getTableElement, getTableObserverFromTableElement, TableCellHeaderStates, TableCellNode, } from '@lexical/table';
|
|
5
|
+
import { mergeRegister } from '@lexical/utils';
|
|
6
|
+
import { $getSelection, $isElementNode, $isRangeSelection, $isTextNode, $setSelection, COMMAND_PRIORITY_CRITICAL, getDOMSelection, isDOMNode, SELECTION_CHANGE_COMMAND, } from 'lexical';
|
|
6
7
|
import { useCallback, useEffect, useRef, useState } from 'react';
|
|
7
8
|
import { createPortal } from 'react-dom';
|
|
8
|
-
import
|
|
9
|
+
import { Box } from '@mui/material';
|
|
9
10
|
import useModal from '../../hooks/useModal';
|
|
10
11
|
import ColorPicker from '../../ui/ColorPicker';
|
|
12
|
+
import DropDown, { DropDownItem } from '../../ui/DropDown';
|
|
11
13
|
function computeSelectionCount(selection) {
|
|
12
14
|
const selectionShape = selection.getShape();
|
|
13
15
|
return {
|
|
@@ -15,41 +17,6 @@ function computeSelectionCount(selection) {
|
|
|
15
17
|
rows: selectionShape.toY - selectionShape.fromY + 1,
|
|
16
18
|
};
|
|
17
19
|
}
|
|
18
|
-
// This is important when merging cells as there is no good way to re-merge weird shapes (a result
|
|
19
|
-
// of selecting merged cells and non-merged)
|
|
20
|
-
function isTableSelectionRectangular(selection) {
|
|
21
|
-
const nodes = selection.getNodes();
|
|
22
|
-
const currentRows = [];
|
|
23
|
-
let currentRow = null;
|
|
24
|
-
let expectedColumns = null;
|
|
25
|
-
let currentColumns = 0;
|
|
26
|
-
for (let i = 0; i < nodes.length; i++) {
|
|
27
|
-
const node = nodes[i];
|
|
28
|
-
if ($isTableCellNode(node)) {
|
|
29
|
-
const row = node.getParentOrThrow();
|
|
30
|
-
invariant($isTableRowNode(row), 'Expected CellNode to have a RowNode parent');
|
|
31
|
-
if (currentRow !== row) {
|
|
32
|
-
if (expectedColumns !== null && currentColumns !== expectedColumns) {
|
|
33
|
-
return false;
|
|
34
|
-
}
|
|
35
|
-
if (currentRow !== null) {
|
|
36
|
-
expectedColumns = currentColumns;
|
|
37
|
-
}
|
|
38
|
-
currentRow = row;
|
|
39
|
-
currentColumns = 0;
|
|
40
|
-
}
|
|
41
|
-
const colSpan = node.__colSpan;
|
|
42
|
-
for (let j = 0; j < colSpan; j++) {
|
|
43
|
-
if (currentRows[currentColumns + j] === undefined) {
|
|
44
|
-
currentRows[currentColumns + j] = 0;
|
|
45
|
-
}
|
|
46
|
-
currentRows[currentColumns + j] += node.__rowSpan;
|
|
47
|
-
}
|
|
48
|
-
currentColumns += colSpan;
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
return ((expectedColumns === null || currentColumns === expectedColumns) && currentRows.every((v) => v === currentRows[0]));
|
|
52
|
-
}
|
|
53
20
|
function $canUnmerge() {
|
|
54
21
|
const selection = $getSelection();
|
|
55
22
|
if (($isRangeSelection(selection) && !selection.isCollapsed()) ||
|
|
@@ -60,16 +27,6 @@ function $canUnmerge() {
|
|
|
60
27
|
const [cell] = $getNodeTriplet(selection.anchor);
|
|
61
28
|
return cell.__colSpan > 1 || cell.__rowSpan > 1;
|
|
62
29
|
}
|
|
63
|
-
function $cellContainsEmptyParagraph(cell) {
|
|
64
|
-
if (cell.getChildrenSize() !== 1) {
|
|
65
|
-
return false;
|
|
66
|
-
}
|
|
67
|
-
const firstChild = cell.getFirstChildOrThrow();
|
|
68
|
-
if (!$isParagraphNode(firstChild) || !firstChild.isEmpty()) {
|
|
69
|
-
return false;
|
|
70
|
-
}
|
|
71
|
-
return true;
|
|
72
|
-
}
|
|
73
30
|
function $selectLastDescendant(node) {
|
|
74
31
|
const lastDescendant = node.getLastDescendant();
|
|
75
32
|
if ($isTextNode(lastDescendant)) {
|
|
@@ -114,7 +71,7 @@ function TableActionMenu({ onClose, tableCellNode: _tableCellNode, setIsMenuOpen
|
|
|
114
71
|
});
|
|
115
72
|
setBackgroundColor(currentCellBackgroundColor(editor) || '');
|
|
116
73
|
}
|
|
117
|
-
});
|
|
74
|
+
}, { skipInitialization: true });
|
|
118
75
|
}, [editor, tableCellNode]);
|
|
119
76
|
useEffect(() => {
|
|
120
77
|
editor.getEditorState().read(() => {
|
|
@@ -123,8 +80,7 @@ function TableActionMenu({ onClose, tableCellNode: _tableCellNode, setIsMenuOpen
|
|
|
123
80
|
if ($isTableSelection(selection)) {
|
|
124
81
|
const currentSelectionCounts = computeSelectionCount(selection);
|
|
125
82
|
updateSelectionCounts(computeSelectionCount(selection));
|
|
126
|
-
setCanMergeCells(
|
|
127
|
-
(currentSelectionCounts.columns > 1 || currentSelectionCounts.rows > 1));
|
|
83
|
+
setCanMergeCells(currentSelectionCounts.columns > 1 || currentSelectionCounts.rows > 1);
|
|
128
84
|
}
|
|
129
85
|
// Unmerge cell
|
|
130
86
|
setCanUnmergeCell($canUnmerge());
|
|
@@ -150,15 +106,16 @@ function TableActionMenu({ onClose, tableCellNode: _tableCellNode, setIsMenuOpen
|
|
|
150
106
|
let topPosition = menuButtonRect.top;
|
|
151
107
|
if (topPosition + dropDownElementRect.height > window.innerHeight) {
|
|
152
108
|
const position = menuButtonRect.bottom - dropDownElementRect.height;
|
|
153
|
-
topPosition =
|
|
109
|
+
topPosition = position < 0 ? margin : position;
|
|
154
110
|
}
|
|
155
|
-
dropDownElement.style.top = `${topPosition
|
|
111
|
+
dropDownElement.style.top = `${topPosition}px`;
|
|
156
112
|
}
|
|
157
113
|
}, [contextRef, dropDownRef, editor]);
|
|
158
114
|
useEffect(() => {
|
|
159
115
|
function handleClickOutside(event) {
|
|
160
116
|
if (dropDownRef.current != null &&
|
|
161
117
|
contextRef.current != null &&
|
|
118
|
+
isDOMNode(event.target) &&
|
|
162
119
|
!dropDownRef.current.contains(event.target) &&
|
|
163
120
|
!contextRef.current.contains(event.target)) {
|
|
164
121
|
setIsMenuOpen(false);
|
|
@@ -171,56 +128,31 @@ function TableActionMenu({ onClose, tableCellNode: _tableCellNode, setIsMenuOpen
|
|
|
171
128
|
editor.update(() => {
|
|
172
129
|
if (tableCellNode.isAttached()) {
|
|
173
130
|
const tableNode = $getTableNodeFromLexicalNodeOrThrow(tableCellNode);
|
|
174
|
-
const tableElement = editor.getElementByKey(tableNode.getKey());
|
|
175
|
-
if (
|
|
176
|
-
throw new Error('Expected to find tableElement in DOM');
|
|
131
|
+
const tableElement = getTableElement(tableNode, editor.getElementByKey(tableNode.getKey()));
|
|
132
|
+
if (tableElement === null) {
|
|
133
|
+
throw new Error('TableActionMenu: Expected to find tableElement in DOM');
|
|
177
134
|
}
|
|
178
|
-
const
|
|
179
|
-
if (
|
|
180
|
-
|
|
135
|
+
const tableObserver = getTableObserverFromTableElement(tableElement);
|
|
136
|
+
if (tableObserver !== null) {
|
|
137
|
+
tableObserver.$clearHighlight();
|
|
181
138
|
}
|
|
182
139
|
tableNode.markDirty();
|
|
183
140
|
updateTableCellNode(tableCellNode.getLatest());
|
|
184
141
|
}
|
|
185
|
-
|
|
186
|
-
rootNode.selectStart();
|
|
142
|
+
$setSelection(null);
|
|
187
143
|
});
|
|
188
144
|
}, [editor, tableCellNode]);
|
|
189
145
|
const mergeTableCellsAtSelection = () => {
|
|
190
146
|
editor.update(() => {
|
|
191
147
|
const selection = $getSelection();
|
|
192
|
-
if (
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
node.setColSpan(columns).setRowSpan(rows);
|
|
201
|
-
firstCell = node;
|
|
202
|
-
const isEmpty = $cellContainsEmptyParagraph(node);
|
|
203
|
-
let firstChild;
|
|
204
|
-
// eslint-disable-next-line no-cond-assign
|
|
205
|
-
if (isEmpty && $isParagraphNode((firstChild = node.getFirstChild()))) {
|
|
206
|
-
firstChild.remove();
|
|
207
|
-
}
|
|
208
|
-
}
|
|
209
|
-
else if ($isTableCellNode(firstCell)) {
|
|
210
|
-
const isEmpty = $cellContainsEmptyParagraph(node);
|
|
211
|
-
if (!isEmpty) {
|
|
212
|
-
firstCell.append(...node.getChildren());
|
|
213
|
-
}
|
|
214
|
-
node.remove();
|
|
215
|
-
}
|
|
216
|
-
}
|
|
217
|
-
}
|
|
218
|
-
if (firstCell !== null) {
|
|
219
|
-
if (firstCell.getChildrenSize() === 0) {
|
|
220
|
-
firstCell.append($createParagraphNode());
|
|
221
|
-
}
|
|
222
|
-
$selectLastDescendant(firstCell);
|
|
223
|
-
}
|
|
148
|
+
if (!$isTableSelection(selection)) {
|
|
149
|
+
return;
|
|
150
|
+
}
|
|
151
|
+
const nodes = selection.getNodes();
|
|
152
|
+
const tableCells = nodes.filter($isTableCellNode);
|
|
153
|
+
const targetCell = $mergeCells(tableCells);
|
|
154
|
+
if (targetCell) {
|
|
155
|
+
$selectLastDescendant(targetCell);
|
|
224
156
|
onClose();
|
|
225
157
|
}
|
|
226
158
|
});
|
|
@@ -232,21 +164,23 @@ function TableActionMenu({ onClose, tableCellNode: _tableCellNode, setIsMenuOpen
|
|
|
232
164
|
};
|
|
233
165
|
const insertTableRowAtSelection = useCallback((shouldInsertAfter) => {
|
|
234
166
|
editor.update(() => {
|
|
235
|
-
|
|
167
|
+
for (let i = 0; i < selectionCounts.rows; i++) {
|
|
168
|
+
$insertTableRowAtSelection(shouldInsertAfter);
|
|
169
|
+
}
|
|
236
170
|
onClose();
|
|
237
171
|
});
|
|
238
|
-
}, [editor, onClose]);
|
|
172
|
+
}, [editor, onClose, selectionCounts.rows]);
|
|
239
173
|
const insertTableColumnAtSelection = useCallback((shouldInsertAfter) => {
|
|
240
174
|
editor.update(() => {
|
|
241
175
|
for (let i = 0; i < selectionCounts.columns; i++) {
|
|
242
|
-
$
|
|
176
|
+
$insertTableColumnAtSelection(shouldInsertAfter);
|
|
243
177
|
}
|
|
244
178
|
onClose();
|
|
245
179
|
});
|
|
246
180
|
}, [editor, onClose, selectionCounts.columns]);
|
|
247
181
|
const deleteTableRowAtSelection = useCallback(() => {
|
|
248
182
|
editor.update(() => {
|
|
249
|
-
$
|
|
183
|
+
$deleteTableRowAtSelection();
|
|
250
184
|
onClose();
|
|
251
185
|
});
|
|
252
186
|
}, [editor, onClose]);
|
|
@@ -260,7 +194,7 @@ function TableActionMenu({ onClose, tableCellNode: _tableCellNode, setIsMenuOpen
|
|
|
260
194
|
}, [editor, tableCellNode, clearTableSelection, onClose]);
|
|
261
195
|
const deleteTableColumnAtSelection = useCallback(() => {
|
|
262
196
|
editor.update(() => {
|
|
263
|
-
$
|
|
197
|
+
$deleteTableColumnAtSelection();
|
|
264
198
|
onClose();
|
|
265
199
|
});
|
|
266
200
|
}, [editor, onClose]);
|
|
@@ -268,20 +202,20 @@ function TableActionMenu({ onClose, tableCellNode: _tableCellNode, setIsMenuOpen
|
|
|
268
202
|
editor.update(() => {
|
|
269
203
|
const tableNode = $getTableNodeFromLexicalNodeOrThrow(tableCellNode);
|
|
270
204
|
const tableRowIndex = $getTableRowIndexFromTableCellNode(tableCellNode);
|
|
271
|
-
const
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
if (
|
|
281
|
-
|
|
205
|
+
const [gridMap] = $computeTableMapSkipCellCheck(tableNode, null, null);
|
|
206
|
+
const rowCells = new Set();
|
|
207
|
+
const newStyle = tableCellNode.getHeaderStyles() ^ TableCellHeaderStates.ROW;
|
|
208
|
+
for (let col = 0; col < gridMap[tableRowIndex].length; col++) {
|
|
209
|
+
const mapCell = gridMap[tableRowIndex][col];
|
|
210
|
+
if (!mapCell?.cell) {
|
|
211
|
+
// eslint-disable-next-line no-continue
|
|
212
|
+
continue;
|
|
213
|
+
}
|
|
214
|
+
if (!rowCells.has(mapCell.cell)) {
|
|
215
|
+
rowCells.add(mapCell.cell);
|
|
216
|
+
mapCell.cell.setHeaderStyles(newStyle, TableCellHeaderStates.ROW);
|
|
282
217
|
}
|
|
283
|
-
|
|
284
|
-
});
|
|
218
|
+
}
|
|
285
219
|
clearTableSelection();
|
|
286
220
|
onClose();
|
|
287
221
|
});
|
|
@@ -290,27 +224,55 @@ function TableActionMenu({ onClose, tableCellNode: _tableCellNode, setIsMenuOpen
|
|
|
290
224
|
editor.update(() => {
|
|
291
225
|
const tableNode = $getTableNodeFromLexicalNodeOrThrow(tableCellNode);
|
|
292
226
|
const tableColumnIndex = $getTableColumnIndexFromTableCellNode(tableCellNode);
|
|
293
|
-
const
|
|
294
|
-
const
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
const tableRow = tableRows[r];
|
|
300
|
-
if (!$isTableRowNode(tableRow)) {
|
|
301
|
-
throw new Error('Expected table row');
|
|
302
|
-
}
|
|
303
|
-
const tableCells = tableRow.getChildren();
|
|
304
|
-
if (tableColumnIndex >= tableCells.length) {
|
|
305
|
-
// if cell is outside of bounds for the current row (for example various merge cell cases) we shouldn't highlight it
|
|
227
|
+
const [gridMap] = $computeTableMapSkipCellCheck(tableNode, null, null);
|
|
228
|
+
const columnCells = new Set();
|
|
229
|
+
const newStyle = tableCellNode.getHeaderStyles() ^ TableCellHeaderStates.COLUMN;
|
|
230
|
+
for (let row = 0; row < gridMap.length; row++) {
|
|
231
|
+
const mapCell = gridMap[row][tableColumnIndex];
|
|
232
|
+
if (!mapCell?.cell) {
|
|
306
233
|
// eslint-disable-next-line no-continue
|
|
307
234
|
continue;
|
|
308
235
|
}
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
236
|
+
if (!columnCells.has(mapCell.cell)) {
|
|
237
|
+
columnCells.add(mapCell.cell);
|
|
238
|
+
mapCell.cell.setHeaderStyles(newStyle, TableCellHeaderStates.COLUMN);
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
clearTableSelection();
|
|
242
|
+
onClose();
|
|
243
|
+
});
|
|
244
|
+
}, [editor, tableCellNode, clearTableSelection, onClose]);
|
|
245
|
+
const toggleRowStriping = useCallback(() => {
|
|
246
|
+
editor.update(() => {
|
|
247
|
+
if (tableCellNode.isAttached()) {
|
|
248
|
+
const tableNode = $getTableNodeFromLexicalNodeOrThrow(tableCellNode);
|
|
249
|
+
if (tableNode) {
|
|
250
|
+
tableNode.setRowStriping(!tableNode.getRowStriping());
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
clearTableSelection();
|
|
254
|
+
onClose();
|
|
255
|
+
});
|
|
256
|
+
}, [editor, tableCellNode, clearTableSelection, onClose]);
|
|
257
|
+
const toggleFirstRowFreeze = useCallback(() => {
|
|
258
|
+
editor.update(() => {
|
|
259
|
+
if (tableCellNode.isAttached()) {
|
|
260
|
+
const tableNode = $getTableNodeFromLexicalNodeOrThrow(tableCellNode);
|
|
261
|
+
if (tableNode) {
|
|
262
|
+
tableNode.setFrozenRows(tableNode.getFrozenRows() === 0 ? 1 : 0);
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
clearTableSelection();
|
|
266
|
+
onClose();
|
|
267
|
+
});
|
|
268
|
+
}, [editor, tableCellNode, clearTableSelection, onClose]);
|
|
269
|
+
const toggleFirstColumnFreeze = useCallback(() => {
|
|
270
|
+
editor.update(() => {
|
|
271
|
+
if (tableCellNode.isAttached()) {
|
|
272
|
+
const tableNode = $getTableNodeFromLexicalNodeOrThrow(tableCellNode);
|
|
273
|
+
if (tableNode) {
|
|
274
|
+
tableNode.setFrozenColumns(tableNode.getFrozenColumns() === 0 ? 1 : 0);
|
|
312
275
|
}
|
|
313
|
-
tableCell.toggleHeaderStyle(TableCellHeaderStates.COLUMN);
|
|
314
276
|
}
|
|
315
277
|
clearTableSelection();
|
|
316
278
|
onClose();
|
|
@@ -336,22 +298,46 @@ function TableActionMenu({ onClose, tableCellNode: _tableCellNode, setIsMenuOpen
|
|
|
336
298
|
}
|
|
337
299
|
});
|
|
338
300
|
}, [editor]);
|
|
301
|
+
const formatVerticalAlign = (value) => {
|
|
302
|
+
editor.update(() => {
|
|
303
|
+
const selection = $getSelection();
|
|
304
|
+
if ($isRangeSelection(selection) || $isTableSelection(selection)) {
|
|
305
|
+
const [cell] = $getNodeTriplet(selection.anchor);
|
|
306
|
+
if ($isTableCellNode(cell)) {
|
|
307
|
+
cell.setVerticalAlign(value);
|
|
308
|
+
}
|
|
309
|
+
if ($isTableSelection(selection)) {
|
|
310
|
+
const nodes = selection.getNodes();
|
|
311
|
+
for (let i = 0; i < nodes.length; i++) {
|
|
312
|
+
const node = nodes[i];
|
|
313
|
+
if ($isTableCellNode(node)) {
|
|
314
|
+
node.setVerticalAlign(value);
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
});
|
|
320
|
+
};
|
|
339
321
|
let mergeCellButton = null;
|
|
340
322
|
if (cellMerge) {
|
|
341
323
|
if (canMergeCells) {
|
|
342
|
-
mergeCellButton = (_jsx("button", { type: "button", className: "item", onClick: () => mergeTableCellsAtSelection(), "data-test-id": "table-merge-cells", children: "Merge cells" }));
|
|
324
|
+
mergeCellButton = (_jsx("button", { type: "button", className: "item", onClick: () => mergeTableCellsAtSelection(), "data-test-id": "table-merge-cells", children: _jsx("span", { className: "text", children: "Merge cells" }) }));
|
|
343
325
|
}
|
|
344
326
|
else if (canUnmergeCell) {
|
|
345
|
-
mergeCellButton = (_jsx("button", { type: "button", className: "item", onClick: () => unmergeTableCellsAtSelection(), "data-test-id": "table-unmerge-cells", children: "Unmerge cells" }));
|
|
327
|
+
mergeCellButton = (_jsx("button", { type: "button", className: "item", onClick: () => unmergeTableCellsAtSelection(), "data-test-id": "table-unmerge-cells", children: _jsx("span", { className: "text", children: "Unmerge cells" }) }));
|
|
346
328
|
}
|
|
347
329
|
}
|
|
348
330
|
return createPortal(
|
|
349
331
|
// eslint-disable-next-line jsx-a11y/no-static-element-interactions
|
|
350
332
|
_jsxs("div", { className: "dropdown", ref: dropDownRef, onClick: (e) => {
|
|
351
333
|
e.stopPropagation();
|
|
352
|
-
}, children: [mergeCellButton, _jsx("button", { type: "button", className: "item", onClick: () => showColorPickerModal('Cell background color', () => (
|
|
353
|
-
|
|
354
|
-
|
|
334
|
+
}, children: [mergeCellButton, _jsx("button", { type: "button", className: "item", onClick: () => showColorPickerModal('Cell background color', () => (_jsx(ColorPicker, { color: backgroundColor, onChange: handleCellBackgroundColor, buttonClassName: "" }))), "data-test-id": "table-background-color", children: _jsx("span", { className: "text", children: "Background color" }) }), _jsx("button", { type: "button", className: "item", onClick: () => toggleRowStriping(), "data-test-id": "table-row-striping", children: _jsx("span", { className: "text", children: "Toggle Row Striping" }) }), _jsxs(DropDown, { buttonLabel: "Vertical Align", buttonClassName: "item", buttonAriaLabel: "Formatting options for vertical alignment", children: [_jsx(DropDownItem, { onClick: () => {
|
|
335
|
+
formatVerticalAlign('top');
|
|
336
|
+
}, className: "item wide", children: _jsxs("div", { className: "icon-text-container", children: [_jsx("i", { className: "icon vertical-top" }), _jsx("span", { className: "text", children: "Top Align" })] }) }), _jsx(DropDownItem, { onClick: () => {
|
|
337
|
+
formatVerticalAlign('middle');
|
|
338
|
+
}, className: "item wide", children: _jsxs("div", { className: "icon-text-container", children: [_jsx("i", { className: "icon vertical-middle" }), _jsx("span", { className: "text", children: "Middle Align" })] }) }), _jsx(DropDownItem, { onClick: () => {
|
|
339
|
+
formatVerticalAlign('bottom');
|
|
340
|
+
}, className: "item wide", children: _jsxs("div", { className: "icon-text-container", children: [_jsx("i", { className: "icon vertical-bottom" }), _jsx("span", { className: "text", children: "Bottom Align" })] }) })] }), _jsx("button", { type: "button", className: "item", onClick: () => toggleFirstRowFreeze(), "data-test-id": "table-freeze-first-row", children: _jsx("span", { className: "text", children: "Toggle First Row Freeze" }) }), _jsx("button", { type: "button", className: "item", onClick: () => toggleFirstColumnFreeze(), "data-test-id": "table-freeze-first-column", children: _jsx("span", { className: "text", children: "Toggle First Column Freeze" }) }), _jsx("hr", {}), _jsx("button", { type: "button", className: "item", onClick: () => insertTableRowAtSelection(false), "data-test-id": "table-insert-row-above", children: _jsxs("span", { className: "text", children: ["Insert ", selectionCounts.rows === 1 ? 'row' : `${selectionCounts.rows} rows`, " above"] }) }), _jsx("button", { type: "button", className: "item", onClick: () => insertTableRowAtSelection(true), "data-test-id": "table-insert-row-below", children: _jsxs("span", { className: "text", children: ["Insert ", selectionCounts.rows === 1 ? 'row' : `${selectionCounts.rows} rows`, " below"] }) }), _jsx("hr", {}), _jsx("button", { type: "button", className: "item", onClick: () => insertTableColumnAtSelection(false), "data-test-id": "table-insert-column-before", children: _jsxs("span", { className: "text", children: ["Insert ", selectionCounts.columns === 1 ? 'column' : `${selectionCounts.columns} columns`, " left"] }) }), _jsx("button", { type: "button", className: "item", onClick: () => insertTableColumnAtSelection(true), "data-test-id": "table-insert-column-after", children: _jsxs("span", { className: "text", children: ["Insert ", selectionCounts.columns === 1 ? 'column' : `${selectionCounts.columns} columns`, " right"] }) }), _jsx("hr", {}), _jsx("button", { type: "button", className: "item", onClick: () => deleteTableColumnAtSelection(), "data-test-id": "table-delete-columns", children: _jsx("span", { className: "text", children: "Delete column" }) }), _jsx("button", { type: "button", className: "item", onClick: () => deleteTableRowAtSelection(), "data-test-id": "table-delete-rows", children: _jsx("span", { className: "text", children: "Delete row" }) }), _jsx("button", { type: "button", className: "item", onClick: () => deleteTableAtSelection(), "data-test-id": "table-delete", children: _jsx("span", { className: "text", children: "Delete table" }) }), _jsx("hr", {}), _jsx("button", { type: "button", className: "item", onClick: () => toggleTableRowIsHeader(), "data-test-id": "table-row-header", children: _jsxs("span", { className: "text", children: [(tableCellNode.__headerState & TableCellHeaderStates.ROW) === TableCellHeaderStates.ROW ? 'Remove' : 'Add', ' ', "row header"] }) }), _jsx("button", { type: "button", className: "item", onClick: () => toggleTableColumnIsHeader(), "data-test-id": "table-column-header", children: _jsxs("span", { className: "text", children: [(tableCellNode.__headerState & TableCellHeaderStates.COLUMN) === TableCellHeaderStates.COLUMN
|
|
355
341
|
? 'Remove'
|
|
356
342
|
: 'Add', ' ', "column header"] }) })] }), document.body);
|
|
357
343
|
}
|
|
@@ -362,62 +348,123 @@ function TableCellActionMenuContainer({ anchorElem, cellMerge, }) {
|
|
|
362
348
|
const [isMenuOpen, setIsMenuOpen] = useState(false);
|
|
363
349
|
const [tableCellNode, setTableMenuCellNode] = useState(null);
|
|
364
350
|
const [colorPickerModal, showColorPickerModal] = useModal();
|
|
365
|
-
const
|
|
351
|
+
const checkTableCellOverflow = useCallback((tableCellParentNodeDOM) => {
|
|
352
|
+
const scrollableContainer = tableCellParentNodeDOM.closest('.PlaygroundEditorTheme__tableScrollableWrapper');
|
|
353
|
+
if (scrollableContainer) {
|
|
354
|
+
const containerRect = scrollableContainer.getBoundingClientRect();
|
|
355
|
+
const cellRect = tableCellParentNodeDOM.getBoundingClientRect();
|
|
356
|
+
// Calculate where the action button would be positioned (5px from right edge of cell)
|
|
357
|
+
// Also account for the button width and table cell padding (8px)
|
|
358
|
+
const actionButtonRight = cellRect.right - 5;
|
|
359
|
+
const actionButtonLeft = actionButtonRight - 28; // 20px width + 8px padding
|
|
360
|
+
// Only hide if the action button would overflow the container
|
|
361
|
+
if (actionButtonRight > containerRect.right || actionButtonLeft < containerRect.left) {
|
|
362
|
+
return true;
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
return false;
|
|
366
|
+
}, []);
|
|
367
|
+
const $moveMenu = useCallback(() => {
|
|
366
368
|
const menu = menuButtonRef.current;
|
|
367
369
|
const selection = $getSelection();
|
|
368
|
-
const nativeSelection =
|
|
370
|
+
const nativeSelection = getDOMSelection(editor._window);
|
|
369
371
|
const { activeElement } = document;
|
|
370
|
-
|
|
372
|
+
function disable() {
|
|
373
|
+
if (menu) {
|
|
374
|
+
menu.classList.remove('table-cell-action-button-container--active');
|
|
375
|
+
menu.classList.add('table-cell-action-button-container--inactive');
|
|
376
|
+
}
|
|
371
377
|
setTableMenuCellNode(null);
|
|
372
|
-
|
|
378
|
+
}
|
|
379
|
+
if (selection == null || menu == null) {
|
|
380
|
+
return disable();
|
|
373
381
|
}
|
|
374
382
|
const rootElement = editor.getRootElement();
|
|
383
|
+
let tableObserver = null;
|
|
384
|
+
let tableCellParentNodeDOM = null;
|
|
375
385
|
if ($isRangeSelection(selection) &&
|
|
376
386
|
rootElement !== null &&
|
|
377
387
|
nativeSelection !== null &&
|
|
378
388
|
rootElement.contains(nativeSelection.anchorNode)) {
|
|
379
389
|
const tableCellNodeFromSelection = $getTableCellNodeFromLexicalNode(selection.anchor.getNode());
|
|
380
390
|
if (tableCellNodeFromSelection == null) {
|
|
381
|
-
|
|
382
|
-
return;
|
|
391
|
+
return disable();
|
|
383
392
|
}
|
|
384
|
-
|
|
385
|
-
if (tableCellParentNodeDOM == null) {
|
|
386
|
-
|
|
387
|
-
|
|
393
|
+
tableCellParentNodeDOM = editor.getElementByKey(tableCellNodeFromSelection.getKey());
|
|
394
|
+
if (tableCellParentNodeDOM == null || !tableCellNodeFromSelection.isAttached()) {
|
|
395
|
+
return disable();
|
|
396
|
+
}
|
|
397
|
+
if (checkTableCellOverflow(tableCellParentNodeDOM)) {
|
|
398
|
+
return disable();
|
|
399
|
+
}
|
|
400
|
+
const tableNode = $getTableNodeFromLexicalNodeOrThrow(tableCellNodeFromSelection);
|
|
401
|
+
const tableElement = getTableElement(tableNode, editor.getElementByKey(tableNode.getKey()));
|
|
402
|
+
if (tableElement === null) {
|
|
403
|
+
throw new Error('TableActionMenu: Expected to find tableElement in DOM');
|
|
388
404
|
}
|
|
405
|
+
tableObserver = getTableObserverFromTableElement(tableElement);
|
|
389
406
|
setTableMenuCellNode(tableCellNodeFromSelection);
|
|
390
407
|
}
|
|
408
|
+
else if ($isTableSelection(selection)) {
|
|
409
|
+
const anchorNode = $getTableCellNodeFromLexicalNode(selection.anchor.getNode());
|
|
410
|
+
if (!$isTableCellNode(anchorNode)) {
|
|
411
|
+
throw new Error('TableSelection anchorNode must be a TableCellNode');
|
|
412
|
+
}
|
|
413
|
+
const tableNode = $getTableNodeFromLexicalNodeOrThrow(anchorNode);
|
|
414
|
+
const tableElement = getTableElement(tableNode, editor.getElementByKey(tableNode.getKey()));
|
|
415
|
+
if (tableElement === null) {
|
|
416
|
+
throw new Error('TableActionMenu: Expected to find tableElement in DOM');
|
|
417
|
+
}
|
|
418
|
+
tableObserver = getTableObserverFromTableElement(tableElement);
|
|
419
|
+
tableCellParentNodeDOM = editor.getElementByKey(anchorNode.getKey());
|
|
420
|
+
if (tableCellParentNodeDOM === null) {
|
|
421
|
+
return disable();
|
|
422
|
+
}
|
|
423
|
+
if (checkTableCellOverflow(tableCellParentNodeDOM)) {
|
|
424
|
+
return disable();
|
|
425
|
+
}
|
|
426
|
+
}
|
|
391
427
|
else if (!activeElement) {
|
|
392
|
-
|
|
428
|
+
return disable();
|
|
393
429
|
}
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
430
|
+
if (tableObserver === null || tableCellParentNodeDOM === null) {
|
|
431
|
+
return disable();
|
|
432
|
+
}
|
|
433
|
+
const enabled = !tableObserver || !tableObserver.isSelecting;
|
|
434
|
+
menu.classList.toggle('table-cell-action-button-container--active', enabled);
|
|
435
|
+
menu.classList.toggle('table-cell-action-button-container--inactive', !enabled);
|
|
436
|
+
if (enabled) {
|
|
437
|
+
const tableCellRect = tableCellParentNodeDOM.getBoundingClientRect();
|
|
438
|
+
const anchorRect = anchorElem.getBoundingClientRect();
|
|
439
|
+
const top = tableCellRect.top - anchorRect.top;
|
|
440
|
+
const left = tableCellRect.right - anchorRect.left;
|
|
441
|
+
menu.style.transform = `translate(${left}px, ${top}px)`;
|
|
442
|
+
}
|
|
443
|
+
}, [editor, anchorElem, checkTableCellOverflow]);
|
|
402
444
|
useEffect(() => {
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
menuButtonDOM.style.transform = `translate(${left}px, ${top}px)`;
|
|
445
|
+
// We call the $moveMenu callback every time the selection changes,
|
|
446
|
+
// once up front, and once after each pointerUp
|
|
447
|
+
let timeoutId;
|
|
448
|
+
const callback = () => {
|
|
449
|
+
timeoutId = undefined;
|
|
450
|
+
editor.getEditorState().read($moveMenu);
|
|
451
|
+
};
|
|
452
|
+
const delayedCallback = () => {
|
|
453
|
+
if (timeoutId === undefined) {
|
|
454
|
+
timeoutId = setTimeout(callback, 0);
|
|
414
455
|
}
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
456
|
+
return false;
|
|
457
|
+
};
|
|
458
|
+
return mergeRegister(editor.registerUpdateListener(delayedCallback), editor.registerCommand(SELECTION_CHANGE_COMMAND, delayedCallback, COMMAND_PRIORITY_CRITICAL), editor.registerRootListener((rootElement, prevRootElement) => {
|
|
459
|
+
if (prevRootElement) {
|
|
460
|
+
prevRootElement.removeEventListener('pointerup', delayedCallback);
|
|
418
461
|
}
|
|
419
|
-
|
|
420
|
-
|
|
462
|
+
if (rootElement) {
|
|
463
|
+
rootElement.addEventListener('pointerup', delayedCallback);
|
|
464
|
+
delayedCallback();
|
|
465
|
+
}
|
|
466
|
+
}), () => clearTimeout(timeoutId));
|
|
467
|
+
});
|
|
421
468
|
const prevTableCellDOM = useRef(tableCellNode);
|
|
422
469
|
useEffect(() => {
|
|
423
470
|
if (prevTableCellDOM.current !== tableCellNode) {
|
|
@@ -425,10 +472,21 @@ function TableCellActionMenuContainer({ anchorElem, cellMerge, }) {
|
|
|
425
472
|
}
|
|
426
473
|
prevTableCellDOM.current = tableCellNode;
|
|
427
474
|
}, [prevTableCellDOM, tableCellNode]);
|
|
428
|
-
return (_jsx("div", { className: "table-cell-action-button-container", ref: menuButtonRef, children: tableCellNode != null && (_jsxs(_Fragment, { children: [_jsx("button",
|
|
475
|
+
return (_jsx("div", { className: "table-cell-action-button-container", ref: menuButtonRef, children: tableCellNode != null && (_jsxs(_Fragment, { children: [_jsx(Box, { component: "button", type: "button", className: "table-cell-action-button chevron-down", onClick: (e) => {
|
|
429
476
|
e.stopPropagation();
|
|
430
477
|
setIsMenuOpen(!isMenuOpen);
|
|
431
|
-
}, ref: menuRootRef,
|
|
478
|
+
}, ref: menuRootRef, sx: {
|
|
479
|
+
display: 'flex',
|
|
480
|
+
alignItems: 'center',
|
|
481
|
+
justifyContent: 'center',
|
|
482
|
+
width: 22,
|
|
483
|
+
height: 22,
|
|
484
|
+
p: 0,
|
|
485
|
+
borderRadius: '100%',
|
|
486
|
+
'& svg': {
|
|
487
|
+
fontSize: 13,
|
|
488
|
+
},
|
|
489
|
+
}, children: _jsx("i", { className: "iconify", "data-icon": "tabler:chevron-down" }) }), colorPickerModal, isMenuOpen && (_jsx(TableActionMenu, { contextRef: menuRootRef, setIsMenuOpen: setIsMenuOpen, onClose: () => setIsMenuOpen(false), tableCellNode: tableCellNode, cellMerge: cellMerge, showColorPickerModal: showColorPickerModal }))] })) }));
|
|
432
490
|
}
|
|
433
491
|
export default function TableActionMenuPlugin({ anchorElem = document.body, cellMerge = false, }) {
|
|
434
492
|
const isEditable = useLexicalEditable();
|
|
@@ -9,6 +9,12 @@
|
|
|
9
9
|
|
|
10
10
|
.TableCellResizer__resizer {
|
|
11
11
|
position: absolute;
|
|
12
|
-
|
|
13
|
-
|
|
12
|
+
touch-action: none;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
@media (pointer: coarse) {
|
|
16
|
+
.TableCellResizer__resizer {
|
|
17
|
+
background-color: #adf;
|
|
18
|
+
mix-blend-mode: color;
|
|
19
|
+
}
|
|
14
20
|
}
|