1 import {EditorBasicButtonDefinition, EditorButtonDefinition} from "../../framework/buttons";
2 import tableIcon from "@icons/editor/table.svg";
3 import deleteIcon from "@icons/editor/table-delete.svg";
4 import deleteColumnIcon from "@icons/editor/table-delete-column.svg";
5 import deleteRowIcon from "@icons/editor/table-delete-row.svg";
6 import insertColumnAfterIcon from "@icons/editor/table-insert-column-after.svg";
7 import insertColumnBeforeIcon from "@icons/editor/table-insert-column-before.svg";
8 import insertRowAboveIcon from "@icons/editor/table-insert-row-above.svg";
9 import insertRowBelowIcon from "@icons/editor/table-insert-row-below.svg";
10 import {EditorUiContext} from "../../framework/core";
11 import {$getSelection, BaseSelection} from "lexical";
12 import {$isCustomTableNode} from "../../../nodes/custom-table";
14 $deleteTableColumn__EXPERIMENTAL,
15 $deleteTableRow__EXPERIMENTAL,
16 $insertTableColumn__EXPERIMENTAL,
17 $insertTableRow__EXPERIMENTAL,
18 $isTableNode, $isTableSelection, $unmergeCell, TableCellNode,
19 } from "@lexical/table";
20 import {$getNodeFromSelection, $selectionContainsNodeType} from "../../../utils/selection";
21 import {$getParentOfType} from "../../../utils/nodes";
22 import {$isCustomTableCellNode} from "../../../nodes/custom-table-cell";
23 import {$showCellPropertiesForm, $showRowPropertiesForm, $showTablePropertiesForm} from "../forms/tables";
25 $clearTableFormatting,
26 $clearTableSizes, $getTableFromSelection,
27 $getTableRowsFromSelection,
28 $mergeTableCellsInSelection
29 } from "../../../utils/tables";
30 import {$isCustomTableRowNode} from "../../../nodes/custom-table-row";
32 $copySelectedColumnsToClipboard,
33 $copySelectedRowsToClipboard,
34 $cutSelectedColumnsToClipboard,
35 $cutSelectedRowsToClipboard,
36 $pasteClipboardRowsBefore,
37 $pasteClipboardRowsAfter,
38 isColumnClipboardEmpty,
40 $pasteClipboardColumnsBefore, $pasteClipboardColumnsAfter
41 } from "../../../utils/table-copy-paste";
43 const neverActive = (): boolean => false;
44 const cellNotSelected = (selection: BaseSelection|null) => !$selectionContainsNodeType(selection, $isCustomTableCellNode);
46 export const table: EditorBasicButtonDefinition = {
51 export const tableProperties: EditorButtonDefinition = {
52 label: 'Table properties',
54 action(context: EditorUiContext) {
55 context.editor.getEditorState().read(() => {
56 const table = $getTableFromSelection($getSelection());
57 if ($isCustomTableNode(table)) {
58 $showTablePropertiesForm(table, context);
62 isActive: neverActive,
63 isDisabled: cellNotSelected,
66 export const clearTableFormatting: EditorButtonDefinition = {
67 label: 'Clear table formatting',
69 action(context: EditorUiContext) {
70 context.editor.update(() => {
71 const cell = $getNodeFromSelection($getSelection(), $isCustomTableCellNode);
72 if (!$isCustomTableCellNode(cell)) {
76 const table = $getParentOfType(cell, $isTableNode);
77 if ($isCustomTableNode(table)) {
78 $clearTableFormatting(table);
82 isActive: neverActive,
83 isDisabled: cellNotSelected,
86 export const resizeTableToContents: EditorButtonDefinition = {
87 label: 'Resize to contents',
89 action(context: EditorUiContext) {
90 context.editor.update(() => {
91 const cell = $getNodeFromSelection($getSelection(), $isCustomTableCellNode);
92 if (!$isCustomTableCellNode(cell)) {
96 const table = $getParentOfType(cell, $isCustomTableNode);
97 if ($isCustomTableNode(table)) {
98 $clearTableSizes(table);
102 isActive: neverActive,
103 isDisabled: cellNotSelected,
106 export const deleteTable: EditorButtonDefinition = {
107 label: 'Delete table',
109 action(context: EditorUiContext) {
110 context.editor.update(() => {
111 const table = $getNodeFromSelection($getSelection(), $isCustomTableNode);
122 export const deleteTableMenuAction: EditorButtonDefinition = {
125 isDisabled(selection) {
126 return !$selectionContainsNodeType(selection, $isTableNode);
130 export const insertRowAbove: EditorButtonDefinition = {
131 label: 'Insert row before',
132 icon: insertRowAboveIcon,
133 action(context: EditorUiContext) {
134 context.editor.update(() => {
135 $insertTableRow__EXPERIMENTAL(false);
138 isActive: neverActive,
139 isDisabled: cellNotSelected,
142 export const insertRowBelow: EditorButtonDefinition = {
143 label: 'Insert row after',
144 icon: insertRowBelowIcon,
145 action(context: EditorUiContext) {
146 context.editor.update(() => {
147 $insertTableRow__EXPERIMENTAL(true);
150 isActive: neverActive,
151 isDisabled: cellNotSelected,
154 export const deleteRow: EditorButtonDefinition = {
157 action(context: EditorUiContext) {
158 context.editor.update(() => {
159 $deleteTableRow__EXPERIMENTAL();
162 isActive: neverActive,
163 isDisabled: cellNotSelected,
166 export const rowProperties: EditorButtonDefinition = {
167 label: 'Row properties',
169 action(context: EditorUiContext) {
170 context.editor.getEditorState().read(() => {
171 const rows = $getTableRowsFromSelection($getSelection());
172 if ($isCustomTableRowNode(rows[0])) {
173 $showRowPropertiesForm(rows[0], context);
177 isActive: neverActive,
178 isDisabled: cellNotSelected,
181 export const cutRow: EditorButtonDefinition = {
184 action(context: EditorUiContext) {
185 context.editor.update(() => {
187 $cutSelectedRowsToClipboard();
193 isActive: neverActive,
194 isDisabled: cellNotSelected,
197 export const copyRow: EditorButtonDefinition = {
200 action(context: EditorUiContext) {
201 context.editor.getEditorState().read(() => {
203 $copySelectedRowsToClipboard();
209 isActive: neverActive,
210 isDisabled: cellNotSelected,
213 export const pasteRowBefore: EditorButtonDefinition = {
214 label: 'Paste row before',
216 action(context: EditorUiContext) {
217 context.editor.update(() => {
219 $pasteClipboardRowsBefore(context.editor);
225 isActive: neverActive,
226 isDisabled: (selection) => cellNotSelected(selection) || isRowClipboardEmpty(),
229 export const pasteRowAfter: EditorButtonDefinition = {
230 label: 'Paste row after',
232 action(context: EditorUiContext) {
233 context.editor.update(() => {
235 $pasteClipboardRowsAfter(context.editor);
241 isActive: neverActive,
242 isDisabled: (selection) => cellNotSelected(selection) || isRowClipboardEmpty(),
245 export const cutColumn: EditorButtonDefinition = {
248 action(context: EditorUiContext) {
249 context.editor.update(() => {
251 $cutSelectedColumnsToClipboard();
257 isActive: neverActive,
258 isDisabled: cellNotSelected,
261 export const copyColumn: EditorButtonDefinition = {
262 label: 'Copy column',
264 action(context: EditorUiContext) {
265 context.editor.getEditorState().read(() => {
267 $copySelectedColumnsToClipboard();
273 isActive: neverActive,
274 isDisabled: cellNotSelected,
277 export const pasteColumnBefore: EditorButtonDefinition = {
278 label: 'Paste column before',
280 action(context: EditorUiContext) {
281 context.editor.update(() => {
283 $pasteClipboardColumnsBefore(context.editor);
289 isActive: neverActive,
290 isDisabled: (selection) => cellNotSelected(selection) || isColumnClipboardEmpty(),
293 export const pasteColumnAfter: EditorButtonDefinition = {
294 label: 'Paste column after',
296 action(context: EditorUiContext) {
297 context.editor.update(() => {
299 $pasteClipboardColumnsAfter(context.editor);
305 isActive: neverActive,
306 isDisabled: (selection) => cellNotSelected(selection) || isColumnClipboardEmpty(),
309 export const insertColumnBefore: EditorButtonDefinition = {
310 label: 'Insert column before',
311 icon: insertColumnBeforeIcon,
312 action(context: EditorUiContext) {
313 context.editor.update(() => {
314 $insertTableColumn__EXPERIMENTAL(false);
322 export const insertColumnAfter: EditorButtonDefinition = {
323 label: 'Insert column after',
324 icon: insertColumnAfterIcon,
325 action(context: EditorUiContext) {
326 context.editor.update(() => {
327 $insertTableColumn__EXPERIMENTAL(true);
335 export const deleteColumn: EditorButtonDefinition = {
336 label: 'Delete column',
337 icon: deleteColumnIcon,
338 action(context: EditorUiContext) {
339 context.editor.update(() => {
340 $deleteTableColumn__EXPERIMENTAL();
348 export const cellProperties: EditorButtonDefinition = {
349 label: 'Cell properties',
351 action(context: EditorUiContext) {
352 context.editor.getEditorState().read(() => {
353 const cell = $getNodeFromSelection($getSelection(), $isCustomTableCellNode);
354 if ($isCustomTableCellNode(cell)) {
355 $showCellPropertiesForm(cell, context);
359 isActive: neverActive,
360 isDisabled: cellNotSelected,
363 export const mergeCells: EditorButtonDefinition = {
364 label: 'Merge cells',
366 action(context: EditorUiContext) {
367 context.editor.update(() => {
368 const selection = $getSelection();
369 if ($isTableSelection(selection)) {
370 $mergeTableCellsInSelection(selection);
374 isActive: neverActive,
375 isDisabled(selection) {
376 return !$isTableSelection(selection);
380 export const splitCell: EditorButtonDefinition = {
383 action(context: EditorUiContext) {
384 context.editor.update(() => {
388 isActive: neverActive,
389 isDisabled(selection) {
390 const cell = $getNodeFromSelection(selection, $isCustomTableCellNode) as TableCellNode|null;
392 const merged = cell.getRowSpan() > 1 || cell.getColSpan() > 1;