]> BookStack Code Mirror - bookstack/commitdiff
Lexical: Linked table form to have caption toggle option
authorDan Brown <redacted>
Wed, 22 Jan 2025 20:39:15 +0000 (20:39 +0000)
committerDan Brown <redacted>
Wed, 22 Jan 2025 20:39:15 +0000 (20:39 +0000)
resources/js/wysiwyg/lexical/table/LexicalCaptionNode.ts
resources/js/wysiwyg/todo.md
resources/js/wysiwyg/ui/defaults/forms/tables.ts
resources/js/wysiwyg/ui/framework/forms.ts

index 08c6870e69fc184d11d7aac29fa05167053aa048..d9d83562c298d887d51d3e806cf0c76aae8b798e 100644 (file)
@@ -1,4 +1,5 @@
 import {
+    $createTextNode,
     DOMConversionMap,
     DOMExportOutput,
     EditorConfig,
@@ -7,6 +8,7 @@ import {
     LexicalNode,
     SerializedElementNode
 } from "lexical";
+import {TableNode} from "@lexical/table/LexicalTableNode";
 
 
 export class CaptionNode extends ElementNode {
@@ -71,4 +73,20 @@ export function $createCaptionNode(): CaptionNode {
 
 export function $isCaptionNode(node: LexicalNode | null | undefined): node is CaptionNode {
     return node instanceof CaptionNode;
+}
+
+export function $tableHasCaption(table: TableNode): boolean {
+    for (const child of table.getChildren()) {
+        if ($isCaptionNode(child)) {
+            return true;
+        }
+    }
+    return false;
+}
+
+export function $addCaptionToTable(table: TableNode, text: string = ''): void {
+    const caption = $createCaptionNode();
+    const textNode = $createTextNode(text || ' ');
+    caption.append(textNode);
+    table.append(caption);
 }
\ No newline at end of file
index 695e8cb69ed02c7220f38e563cae46f7785bed13..1d42ba3e4474206801bde671ebc5b6ff5f3fdf0f 100644 (file)
@@ -10,7 +10,6 @@
 
 ## Secondary Todo
 
-- Table caption text support
 - Support media src conversions (https://github.com/tinymce/tinymce/blob/release/6.6/modules/tinymce/src/plugins/media/main/ts/core/UrlPatterns.ts)
 - Deep check of translation coverage
 
index b592d7c67e65911d2de5f50e044d564d9506c5ec..5b484310d9a52bb11add68aa26335a0d6944e446 100644 (file)
@@ -18,6 +18,7 @@ import {formatSizeValue} from "../../../utils/dom";
 import {TableCellNode, TableNode, TableRowNode} from "@lexical/table";
 import {CommonBlockAlignment} from "lexical/nodes/common";
 import {colorFieldBuilder} from "../../framework/blocks/color-field";
+import {$addCaptionToTable, $isCaptionNode, $tableHasCaption} from "@lexical/table/LexicalCaptionNode";
 
 const borderStyleInput: EditorSelectFormFieldDefinition = {
     label: 'Border style',
@@ -219,6 +220,7 @@ export const rowProperties: EditorFormDefinition = {
 export function $showTablePropertiesForm(table: TableNode, context: EditorUiContext): EditorFormModal {
     const styles = table.getStyles();
     const modalForm = context.manager.createModal('table_properties');
+
     modalForm.show({
         width: styles.get('width') || '',
         height: styles.get('height') || '',
@@ -228,7 +230,7 @@ export function $showTablePropertiesForm(table: TableNode, context: EditorUiCont
         border_style: styles.get('border-style') || '',
         border_color: styles.get('border-color') || '',
         background_color: styles.get('background-color') || '',
-        // caption: '', TODO
+        caption: $tableHasCaption(table) ? 'true' : '',
         align: table.getAlignment(),
     });
     return modalForm;
@@ -265,7 +267,17 @@ export const tableProperties: EditorFormDefinition = {
                 });
             }
 
-            // TODO - cell caption
+            const showCaption = Boolean(formData.get('caption')?.toString() || '');
+            const hasCaption = $tableHasCaption(table);
+            if (showCaption && !hasCaption) {
+                $addCaptionToTable(table, context.translate('Caption'));
+            } else if (!showCaption && hasCaption) {
+                for (const child of table.getChildren()) {
+                    if ($isCaptionNode(child)) {
+                        child.remove();
+                    }
+                }
+            }
         });
         return true;
     },
@@ -299,9 +311,9 @@ export const tableProperties: EditorFormDefinition = {
                         type: 'text',
                     },
                     {
-                        label: 'caption', // Caption element
+                        label: 'Show caption', // Caption element
                         name: 'caption',
-                        type: 'text', // TODO -
+                        type: 'checkbox',
                     },
                     alignmentInput, // alignment class
                 ];
index 771ab0bdfe5e7f4c8a9a3d760f8f3ac2984e2eee..08edb214e2a780f7854ba59f2b41ae02041bf098 100644 (file)
@@ -11,7 +11,7 @@ import {el} from "../../utils/dom";
 export interface EditorFormFieldDefinition {
     label: string;
     name: string;
-    type: 'text' | 'select' | 'textarea';
+    type: 'text' | 'select' | 'textarea' | 'checkbox';
 }
 
 export interface EditorSelectFormFieldDefinition extends EditorFormFieldDefinition {
@@ -42,7 +42,11 @@ export class EditorFormField extends EditorUiElement {
 
     setValue(value: string) {
         const input = this.getDOMElement().querySelector('input,select,textarea') as HTMLInputElement;
-        input.value = value;
+        if (this.definition.type === 'checkbox') {
+            input.checked = Boolean(value);
+        } else {
+            input.value = value;
+        }
         input.dispatchEvent(new Event('change'));
     }
 
@@ -61,6 +65,8 @@ export class EditorFormField extends EditorUiElement {
             input = el('select', {id, name: this.definition.name, class: 'editor-form-field-input'}, optionElems);
         } else if (this.definition.type === 'textarea') {
             input = el('textarea', {id, name: this.definition.name, class: 'editor-form-field-input'});
+        } else if (this.definition.type === 'checkbox') {
+            input = el('input', {id, name: this.definition.name, type: 'checkbox', class: 'editor-form-field-input-checkbox', value: 'true'});
         } else {
             input = el('input', {id, name: this.definition.name, class: 'editor-form-field-input'});
         }
Morty Proxy This is a proxified and sanitized view of the page, visit original site.