]> BookStack Code Mirror - bookstack/blob - resources/js/wysiwyg/ui/defaults/forms/tables.ts
Lexical: Merged custom table node code
[bookstack] / resources / js / wysiwyg / ui / defaults / forms / tables.ts
1 import {
2     EditorFormDefinition,
3     EditorFormFieldDefinition,
4     EditorFormTabs,
5     EditorSelectFormFieldDefinition
6 } from "../../framework/forms";
7 import {EditorUiContext} from "../../framework/core";
8 import {EditorFormModal} from "../../framework/modals";
9 import {$getSelection, ElementFormatType} from "lexical";
10 import {
11     $forEachTableCell, $getCellPaddingForTable,
12     $getTableCellColumnWidth,
13     $getTableCellsFromSelection, $getTableFromSelection,
14     $getTableRowsFromSelection,
15     $setTableCellColumnWidth
16 } from "../../../utils/tables";
17 import {formatSizeValue} from "../../../utils/dom";
18 import {TableCellNode, TableNode, TableRowNode} from "@lexical/table";
19 import {CommonBlockAlignment} from "../../../nodes/_common";
20
21 const borderStyleInput: EditorSelectFormFieldDefinition = {
22     label: 'Border style',
23     name: 'border_style',
24     type: 'select',
25     valuesByLabel: {
26         'Select...': '',
27         "Solid": 'solid',
28         "Dotted": 'dotted',
29         "Dashed": 'dashed',
30         "Double": 'double',
31         "Groove": 'groove',
32         "Ridge": 'ridge',
33         "Inset": 'inset',
34         "Outset": 'outset',
35         "None": 'none',
36         "Hidden": 'hidden',
37     }
38 };
39
40 const borderColorInput: EditorFormFieldDefinition = {
41     label: 'Border color',
42     name: 'border_color',
43     type: 'text',
44 };
45
46 const backgroundColorInput: EditorFormFieldDefinition = {
47     label: 'Background color',
48     name: 'background_color',
49     type: 'text',
50 };
51
52 const alignmentInput: EditorSelectFormFieldDefinition = {
53     label: 'Alignment',
54     name: 'align',
55     type: 'select',
56     valuesByLabel: {
57         'None': '',
58         'Left': 'left',
59         'Center': 'center',
60         'Right': 'right',
61     }
62 };
63
64 export function $showCellPropertiesForm(cell: TableCellNode, context: EditorUiContext): EditorFormModal {
65     const styles = cell.getStyles();
66     const modalForm = context.manager.createModal('cell_properties');
67     modalForm.show({
68         width: $getTableCellColumnWidth(context.editor, cell),
69         height: styles.get('height') || '',
70         type: cell.getTag(),
71         h_align: cell.getAlignment(),
72         v_align: styles.get('vertical-align') || '',
73         border_width: styles.get('border-width') || '',
74         border_style: styles.get('border-style') || '',
75         border_color: styles.get('border-color') || '',
76         background_color: styles.get('background-color') || '',
77     });
78     return modalForm;
79 }
80
81 export const cellProperties: EditorFormDefinition = {
82     submitText: 'Save',
83     async action(formData, context: EditorUiContext) {
84         context.editor.update(() => {
85             const cells = $getTableCellsFromSelection($getSelection());
86             for (const cell of cells) {
87                 const width = formData.get('width')?.toString() || '';
88
89                 $setTableCellColumnWidth(cell, width);
90                 cell.updateTag(formData.get('type')?.toString() || '');
91                 cell.setAlignment((formData.get('h_align')?.toString() || '') as CommonBlockAlignment);
92
93                 const styles = cell.getStyles();
94                 styles.set('height', formatSizeValue(formData.get('height')?.toString() || ''));
95                 styles.set('vertical-align', formData.get('v_align')?.toString() || '');
96                 styles.set('border-width', formatSizeValue(formData.get('border_width')?.toString() || ''));
97                 styles.set('border-style', formData.get('border_style')?.toString() || '');
98                 styles.set('border-color', formData.get('border_color')?.toString() || '');
99                 styles.set('background-color', formData.get('background_color')?.toString() || '');
100
101                 cell.setStyles(styles);
102             }
103         });
104
105         return true;
106     },
107     fields: [
108         {
109             build() {
110                 const generalFields: EditorFormFieldDefinition[] = [
111                     {
112                         label: 'Width', // Colgroup width
113                         name: 'width',
114                         type: 'text',
115                     },
116                     {
117                         label: 'Height', // inline-style: height
118                         name: 'height',
119                         type: 'text',
120                     },
121                     {
122                         label: 'Cell type', // element
123                         name: 'type',
124                         type: 'select',
125                         valuesByLabel: {
126                             'Cell': 'td',
127                             'Header cell': 'th',
128                         }
129                     } as EditorSelectFormFieldDefinition,
130                     {
131                         ...alignmentInput, // class: 'align-right/left/center'
132                         label: 'Horizontal align',
133                         name: 'h_align',
134                     },
135                     {
136                         label: 'Vertical align', // inline-style: vertical-align
137                         name: 'v_align',
138                         type: 'select',
139                         valuesByLabel: {
140                             'None': '',
141                             'Top': 'top',
142                             'Middle': 'middle',
143                             'Bottom': 'bottom',
144                         }
145                     } as EditorSelectFormFieldDefinition,
146                 ];
147
148                 const advancedFields: EditorFormFieldDefinition[] = [
149                     {
150                         label: 'Border width', // inline-style: border-width
151                         name: 'border_width',
152                         type: 'text',
153                     },
154                     borderStyleInput, // inline-style: border-style
155                     borderColorInput, // inline-style: border-color
156                     backgroundColorInput, // inline-style: background-color
157                 ];
158
159                 return new EditorFormTabs([
160                     {
161                         label: 'General',
162                         contents: generalFields,
163                     },
164                     {
165                         label: 'Advanced',
166                         contents: advancedFields,
167                     }
168                 ])
169             }
170         },
171     ],
172 };
173
174 export function $showRowPropertiesForm(row: TableRowNode, context: EditorUiContext): EditorFormModal {
175     const styles = row.getStyles();
176     const modalForm = context.manager.createModal('row_properties');
177     modalForm.show({
178         height: styles.get('height') || '',
179         border_style: styles.get('border-style') || '',
180         border_color: styles.get('border-color') || '',
181         background_color: styles.get('background-color') || '',
182     });
183     return modalForm;
184 }
185
186 export const rowProperties: EditorFormDefinition = {
187     submitText: 'Save',
188     async action(formData, context: EditorUiContext) {
189         context.editor.update(() => {
190             const rows = $getTableRowsFromSelection($getSelection());
191             for (const row of rows) {
192                 const styles = row.getStyles();
193                 styles.set('height', formatSizeValue(formData.get('height')?.toString() || ''));
194                 styles.set('border-style', formData.get('border_style')?.toString() || '');
195                 styles.set('border-color', formData.get('border_color')?.toString() || '');
196                 styles.set('background-color', formData.get('background_color')?.toString() || '');
197                 row.setStyles(styles);
198             }
199         });
200         return true;
201     },
202     fields: [
203         // Removed fields:
204         // Removed 'Row Type' as we don't currently support thead/tfoot elements
205         //  TinyMCE would move rows up/down into these parents when set
206         // Removed 'Alignment' since this was broken in our editor (applied alignment class to whole parent table)
207         {
208             label: 'Height', // style on tr: height
209             name: 'height',
210             type: 'text',
211         },
212         borderStyleInput, // style on tr: height
213         borderColorInput, // style on tr: height
214         backgroundColorInput, // style on tr: height
215     ],
216 };
217
218 export function $showTablePropertiesForm(table: TableNode, context: EditorUiContext): EditorFormModal {
219     const styles = table.getStyles();
220     const modalForm = context.manager.createModal('table_properties');
221     modalForm.show({
222         width: styles.get('width') || '',
223         height: styles.get('height') || '',
224         cell_spacing: styles.get('cell-spacing') || '',
225         cell_padding: $getCellPaddingForTable(table),
226         border_width: styles.get('border-width') || '',
227         border_style: styles.get('border-style') || '',
228         border_color: styles.get('border-color') || '',
229         background_color: styles.get('background-color') || '',
230         // caption: '', TODO
231         align: table.getAlignment(),
232     });
233     return modalForm;
234 }
235
236 export const tableProperties: EditorFormDefinition = {
237     submitText: 'Save',
238     async action(formData, context: EditorUiContext) {
239         context.editor.update(() => {
240             const table = $getTableFromSelection($getSelection());
241             if (!table) {
242                 return;
243             }
244
245             const styles = table.getStyles();
246             styles.set('width', formatSizeValue(formData.get('width')?.toString() || ''));
247             styles.set('height', formatSizeValue(formData.get('height')?.toString() || ''));
248             styles.set('cell-spacing', formatSizeValue(formData.get('cell_spacing')?.toString() || ''));
249             styles.set('border-width', formatSizeValue(formData.get('border_width')?.toString() || ''));
250             styles.set('border-style', formData.get('border_style')?.toString() || '');
251             styles.set('border-color', formData.get('border_color')?.toString() || '');
252             styles.set('background-color', formData.get('background_color')?.toString() || '');
253             table.setStyles(styles);
254
255             table.setAlignment(formData.get('align') as CommonBlockAlignment);
256
257             const cellPadding = (formData.get('cell_padding')?.toString() || '');
258             if (cellPadding) {
259                 const cellPaddingFormatted = formatSizeValue(cellPadding);
260                 $forEachTableCell(table, (cell: TableCellNode) => {
261                     const styles = cell.getStyles();
262                     styles.set('padding', cellPaddingFormatted);
263                     cell.setStyles(styles);
264                 });
265             }
266
267             // TODO - cell caption
268         });
269         return true;
270     },
271     fields: [
272         {
273             build() {
274                 const generalFields: EditorFormFieldDefinition[] = [
275                     {
276                         label: 'Width', // Style - width
277                         name: 'width',
278                         type: 'text',
279                     },
280                     {
281                         label: 'Height', // Style - height
282                         name: 'height',
283                         type: 'text',
284                     },
285                     {
286                         label: 'Cell spacing', // Style - border-spacing
287                         name: 'cell_spacing',
288                         type: 'text',
289                     },
290                     {
291                         label: 'Cell padding', // Style - padding on child cells?
292                         name: 'cell_padding',
293                         type: 'text',
294                     },
295                     {
296                         label: 'Border width', // Style - border-width
297                         name: 'border_width',
298                         type: 'text',
299                     },
300                     {
301                         label: 'caption', // Caption element
302                         name: 'caption',
303                         type: 'text', // TODO -
304                     },
305                     alignmentInput, // alignment class
306                 ];
307
308                 const advancedFields: EditorFormFieldDefinition[] = [
309                     borderStyleInput, // Style - border-style
310                     borderColorInput, // Style - border-color
311                     backgroundColorInput, // Style - background-color
312                 ];
313
314                 return new EditorFormTabs([
315                     {
316                         label: 'General',
317                         contents: generalFields,
318                     },
319                     {
320                         label: 'Advanced',
321                         contents: advancedFields,
322                     }
323                 ])
324             }
325         },
326     ],
327 };