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