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