]> BookStack Code Mirror - bookstack/blob - resources/js/wysiwyg/ui/defaults/buttons/tables.ts
Lexical: Split helpers to utils, refactored files
[bookstack] / resources / js / wysiwyg / ui / defaults / buttons / tables.ts
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";
13 import {
14     $createTableRowNode,
15     $deleteTableColumn__EXPERIMENTAL,
16     $deleteTableRow__EXPERIMENTAL,
17     $insertTableColumn__EXPERIMENTAL,
18     $insertTableRow__EXPERIMENTAL, $isTableCellNode,
19     $isTableNode, $isTableRowNode, $isTableSelection, $unmergeCell, TableCellNode, TableNode,
20 } from "@lexical/table";
21 import {$getNodeFromSelection, $selectionContainsNodeType} from "../../../utils/selection";
22 import {$getParentOfType} from "../../../utils/nodes";
23
24 const neverActive = (): boolean => false;
25 const cellNotSelected = (selection: BaseSelection|null) => !$selectionContainsNodeType(selection, $isTableCellNode);
26
27 export const table: EditorBasicButtonDefinition = {
28     label: 'Table',
29     icon: tableIcon,
30 };
31
32 export const tableProperties: EditorButtonDefinition = {
33     label: 'Table properties',
34     icon: tableIcon,
35     action(context: EditorUiContext) {
36         context.editor.getEditorState().read(() => {
37             const cell = $getNodeFromSelection($getSelection(), $isTableCellNode);
38             if (!$isTableCellNode(cell)) {
39                 return;
40             }
41
42             const table = $getParentOfType(cell, $isTableNode);
43             const modalForm = context.manager.createModal('table_properties');
44             modalForm.show({});
45             // TODO
46         });
47     },
48     isActive: neverActive,
49     isDisabled: cellNotSelected,
50 };
51
52 export const clearTableFormatting: EditorButtonDefinition = {
53     label: 'Clear table formatting',
54     format: 'long',
55     action(context: EditorUiContext) {
56         context.editor.getEditorState().read(() => {
57             const cell = $getNodeFromSelection($getSelection(), $isTableCellNode);
58             if (!$isTableCellNode(cell)) {
59                 return;
60             }
61
62             const table = $getParentOfType(cell, $isTableNode);
63             // TODO
64         });
65     },
66     isActive: neverActive,
67     isDisabled: cellNotSelected,
68 };
69
70 export const resizeTableToContents: EditorButtonDefinition = {
71     label: 'Resize to contents',
72     format: 'long',
73     action(context: EditorUiContext) {
74         context.editor.getEditorState().read(() => {
75             const cell = $getNodeFromSelection($getSelection(), $isTableCellNode);
76             if (!$isTableCellNode(cell)) {
77                 return;
78             }
79
80             const table = $getParentOfType(cell, $isCustomTableNode);
81             if (!$isCustomTableNode(table)) {
82                 return;
83             }
84
85             for (const row of table.getChildren()) {
86                 if ($isTableRowNode(row)) {
87                     // TODO - Come back later as this may depend on if we
88                     //   are using a custom table row
89                 }
90             }
91         });
92     },
93     isActive: neverActive,
94     isDisabled: cellNotSelected,
95 };
96
97 export const deleteTable: EditorButtonDefinition = {
98     label: 'Delete table',
99     icon: deleteIcon,
100     action(context: EditorUiContext) {
101         context.editor.update(() => {
102             const table = $getNodeFromSelection($getSelection(), $isCustomTableNode);
103             if (table) {
104                 table.remove();
105             }
106         });
107     },
108     isActive() {
109         return false;
110     }
111 };
112
113 export const deleteTableMenuAction: EditorButtonDefinition = {
114     ...deleteTable,
115     format: 'long',
116     isDisabled(selection) {
117         return !$selectionContainsNodeType(selection, $isTableNode);
118     },
119 };
120
121 export const insertRowAbove: EditorButtonDefinition = {
122     label: 'Insert row before',
123     icon: insertRowAboveIcon,
124     action(context: EditorUiContext) {
125         context.editor.update(() => {
126             $insertTableRow__EXPERIMENTAL(false);
127         });
128     },
129     isActive: neverActive,
130     isDisabled: cellNotSelected,
131 };
132
133 export const insertRowBelow: EditorButtonDefinition = {
134     label: 'Insert row after',
135     icon: insertRowBelowIcon,
136     action(context: EditorUiContext) {
137         context.editor.update(() => {
138             $insertTableRow__EXPERIMENTAL(true);
139         });
140     },
141     isActive: neverActive,
142     isDisabled: cellNotSelected,
143 };
144
145 export const deleteRow: EditorButtonDefinition = {
146     label: 'Delete row',
147     icon: deleteRowIcon,
148     action(context: EditorUiContext) {
149         context.editor.update(() => {
150             $deleteTableRow__EXPERIMENTAL();
151         });
152     },
153     isActive: neverActive,
154     isDisabled: cellNotSelected,
155 };
156
157 export const rowProperties: EditorButtonDefinition = {
158     label: 'Row properties',
159     format: 'long',
160     action(context: EditorUiContext) {
161         context.editor.getEditorState().read(() => {
162             const cell = $getNodeFromSelection($getSelection(), $isTableCellNode);
163             if (!$isTableCellNode(cell)) {
164                 return;
165             }
166
167             const row = $getParentOfType(cell, $isTableRowNode);
168             const modalForm = context.manager.createModal('row_properties');
169             modalForm.show({});
170             // TODO
171         });
172     },
173     isActive: neverActive,
174     isDisabled: cellNotSelected,
175 };
176
177 export const cutRow: EditorButtonDefinition = {
178     label: 'Cut row',
179     format: 'long',
180     action(context: EditorUiContext) {
181         context.editor.getEditorState().read(() => {
182             // TODO
183         });
184     },
185     isActive: neverActive,
186     isDisabled: cellNotSelected,
187 };
188
189 export const copyRow: EditorButtonDefinition = {
190     label: 'Copy row',
191     format: 'long',
192     action(context: EditorUiContext) {
193         context.editor.getEditorState().read(() => {
194             // TODO
195         });
196     },
197     isActive: neverActive,
198     isDisabled: cellNotSelected,
199 };
200
201 export const pasteRowBefore: EditorButtonDefinition = {
202     label: 'Paste row before',
203     format: 'long',
204     action(context: EditorUiContext) {
205         context.editor.getEditorState().read(() => {
206             // TODO
207         });
208     },
209     isActive: neverActive,
210     isDisabled: cellNotSelected,
211 };
212
213 export const pasteRowAfter: EditorButtonDefinition = {
214     label: 'Paste row after',
215     format: 'long',
216     action(context: EditorUiContext) {
217         context.editor.getEditorState().read(() => {
218             // TODO
219         });
220     },
221     isActive: neverActive,
222     isDisabled: cellNotSelected,
223 };
224
225 export const cutColumn: EditorButtonDefinition = {
226     label: 'Cut column',
227     format: 'long',
228     action(context: EditorUiContext) {
229         context.editor.getEditorState().read(() => {
230             // TODO
231         });
232     },
233     isActive: neverActive,
234     isDisabled: cellNotSelected,
235 };
236
237 export const copyColumn: EditorButtonDefinition = {
238     label: 'Copy column',
239     format: 'long',
240     action(context: EditorUiContext) {
241         context.editor.getEditorState().read(() => {
242             // TODO
243         });
244     },
245     isActive: neverActive,
246     isDisabled: cellNotSelected,
247 };
248
249 export const pasteColumnBefore: EditorButtonDefinition = {
250     label: 'Paste column before',
251     format: 'long',
252     action(context: EditorUiContext) {
253         context.editor.getEditorState().read(() => {
254             // TODO
255         });
256     },
257     isActive: neverActive,
258     isDisabled: cellNotSelected,
259 };
260
261 export const pasteColumnAfter: EditorButtonDefinition = {
262     label: 'Paste column after',
263     format: 'long',
264     action(context: EditorUiContext) {
265         context.editor.getEditorState().read(() => {
266             // TODO
267         });
268     },
269     isActive: neverActive,
270     isDisabled: cellNotSelected,
271 };
272
273 export const insertColumnBefore: EditorButtonDefinition = {
274     label: 'Insert column before',
275     icon: insertColumnBeforeIcon,
276     action(context: EditorUiContext) {
277         context.editor.update(() => {
278             $insertTableColumn__EXPERIMENTAL(false);
279         });
280     },
281     isActive() {
282         return false;
283     }
284 };
285
286 export const insertColumnAfter: EditorButtonDefinition = {
287     label: 'Insert column after',
288     icon: insertColumnAfterIcon,
289     action(context: EditorUiContext) {
290         context.editor.update(() => {
291             $insertTableColumn__EXPERIMENTAL(true);
292         });
293     },
294     isActive() {
295         return false;
296     }
297 };
298
299 export const deleteColumn: EditorButtonDefinition = {
300     label: 'Delete column',
301     icon: deleteColumnIcon,
302     action(context: EditorUiContext) {
303         context.editor.update(() => {
304             $deleteTableColumn__EXPERIMENTAL();
305         });
306     },
307     isActive() {
308         return false;
309     }
310 };
311
312 export const cellProperties: EditorButtonDefinition = {
313     label: 'Cell properties',
314     action(context: EditorUiContext) {
315         context.editor.getEditorState().read(() => {
316             const cell = $getNodeFromSelection($getSelection(), $isTableCellNode);
317             if ($isTableCellNode(cell)) {
318
319                 const modalForm = context.manager.createModal('cell_properties');
320                 modalForm.show({});
321             }
322         });
323     },
324     isActive: neverActive,
325     isDisabled: cellNotSelected,
326 };
327
328 export const mergeCells: EditorButtonDefinition = {
329     label: 'Merge cells',
330     action(context: EditorUiContext) {
331         context.editor.update(() => {
332             // Todo - Needs to be done manually
333             // Playground reference:
334             // https://github.com/facebook/lexical/blob/f373759a7849f473d34960a6bf4e34b2a011e762/packages/lexical-playground/src/plugins/TableActionMenuPlugin/index.tsx#L299
335         });
336     },
337     isActive: neverActive,
338     isDisabled(selection) {
339         return !$isTableSelection(selection);
340     }
341 };
342
343 export const splitCell: EditorButtonDefinition = {
344     label: 'Split cell',
345     action(context: EditorUiContext) {
346         context.editor.update(() => {
347             $unmergeCell();
348         });
349     },
350     isActive: neverActive,
351     isDisabled(selection) {
352         const cell = $getNodeFromSelection(selection, $isTableCellNode) as TableCellNode|null;
353         if (cell) {
354             const merged = cell.getRowSpan() > 1 || cell.getColSpan() > 1;
355             return !merged;
356         }
357
358         return true;
359     }
360 };