Просмотр исходного кода

Move responsibility for parsing and validating table data out of the tabular widget (#15152)

GitOrigin-RevId: bba823ca036501a527f08746cffb155641d3becf
Alf Eaton 2 лет назад
Родитель
Сommit
4f5ca6624e

+ 21 - 0
services/web/frontend/js/features/source-editor/components/table-generator/utils.ts

@@ -451,6 +451,27 @@ export function generateTable(
   }
 }
 
+export const validateParsedTable = (parseResult: ParsedTableData) => {
+  for (const row of parseResult.table.rows) {
+    const rowLength = row.cells.reduce(
+      (acc, cell) => acc + (cell.multiColumn?.columnSpan ?? 1),
+      0
+    )
+    for (const cell of row.cells) {
+      if (
+        cell.multiColumn?.columns.specification &&
+        cell.multiColumn.columns.specification.length !== 1
+      ) {
+        return false
+      }
+    }
+    if (rowLength !== parseResult.table.columns.length) {
+      return false
+    }
+  }
+  return true
+}
+
 export function parseTableEnvironment(tableNode: SyntaxNode) {
   const tableEnvironment: TableEnvironmentData = {
     table: { from: tableNode.from, to: tableNode.to },

+ 26 - 12
services/web/frontend/js/features/source-editor/extensions/visual/atomic-decorations.ts

@@ -67,6 +67,12 @@ import { TableRenderingErrorWidget } from './visual-widgets/table-rendering-erro
 import { GraphicsWidget } from './visual-widgets/graphics'
 import { InlineGraphicsWidget } from './visual-widgets/inline-graphics'
 import { PreviewPath } from '../../../../../../types/preview-path'
+import {
+  generateTable,
+  ParsedTableData,
+  validateParsedTable,
+} from '../../components/table-generator/utils'
+import { debugConsole } from '@/utils/debugging'
 
 type Options = {
   previewByPath: (path: string) => PreviewPath | null
@@ -324,21 +330,29 @@ export const atomicDecorations = (options: Options) => {
                 tabularNode.parent,
                 tableNode
               )
-              const tabularWidget = new TabularWidget(
-                tabularNode,
-                state.doc.sliceString(
-                  (tableNode ?? tabularNode).from,
-                  (tableNode ?? tabularNode).to
-                ),
-                tableNode,
-                directChild,
-                state
-              )
 
-              if (tabularWidget.isValid()) {
+              let parsedTableData: ParsedTableData | null = null
+              let validTable = false
+              try {
+                parsedTableData = generateTable(tabularNode, state)
+                validTable = validateParsedTable(parsedTableData)
+              } catch (e) {
+                debugConsole.error(e)
+              }
+
+              if (parsedTableData && validTable) {
                 decorations.push(
                   Decoration.replace({
-                    widget: tabularWidget,
+                    widget: new TabularWidget(
+                      parsedTableData,
+                      tabularNode,
+                      state.doc.sliceString(
+                        (tableNode ?? tabularNode).from,
+                        (tableNode ?? tabularNode).to
+                      ),
+                      tableNode,
+                      directChild
+                    ),
                     block: true,
                   }).range(nodeRef.from, nodeRef.to)
                 )

+ 25 - 69
services/web/frontend/js/features/source-editor/extensions/visual/visual-widgets/tabular.tsx

@@ -1,77 +1,37 @@
 import { EditorView, WidgetType } from '@codemirror/view'
-import { EditorState } from '@codemirror/state'
 import { SyntaxNode } from '@lezer/common'
 import * as ReactDOM from 'react-dom'
 import { Tabular } from '../../../components/table-generator/tabular'
-import {
-  ParsedTableData,
-  generateTable,
-} from '../../../components/table-generator/utils'
-import { debugConsole } from '@/utils/debugging'
+import { ParsedTableData } from '../../../components/table-generator/utils'
 
 export class TabularWidget extends WidgetType {
-  private element: HTMLElement | undefined
-  private readonly parseResult: ParsedTableData | null = null
-
   constructor(
+    private parsedTableData: ParsedTableData,
     private tabularNode: SyntaxNode,
     private content: string,
     private tableNode: SyntaxNode | null,
-    private isDirectChildOfTableEnvironment: boolean,
-    state: EditorState
+    private isDirectChildOfTableEnvironment: boolean
   ) {
     super()
-    try {
-      this.parseResult = generateTable(tabularNode, state)
-    } catch (e) {
-      debugConsole.error(e)
-      this.parseResult = null
-    }
-  }
-
-  isValid() {
-    if (!this.parseResult) {
-      return false
-    }
-    for (const row of this.parseResult.table.rows) {
-      const rowLength = row.cells.reduce(
-        (acc, cell) => acc + (cell.multiColumn?.columnSpan ?? 1),
-        0
-      )
-      for (const cell of row.cells) {
-        if (
-          cell.multiColumn?.columns.specification &&
-          cell.multiColumn.columns.specification.length !== 1
-        ) {
-          return false
-        }
-      }
-      if (rowLength !== this.parseResult.table.columns.length) {
-        return false
-      }
-    }
-    return true
   }
 
   toDOM(view: EditorView) {
-    this.element = document.createElement('div')
-    this.element.classList.add('ol-cm-tabular')
+    const element = document.createElement('div')
+    element.classList.add('ol-cm-tabular')
     if (this.tableNode) {
-      this.element.classList.add('ol-cm-environment-table')
+      element.classList.add('ol-cm-environment-table')
     }
-    if (this.parseResult) {
-      ReactDOM.render(
-        <Tabular
-          view={view}
-          tabularNode={this.tabularNode}
-          parsedTableData={this.parseResult}
-          tableNode={this.tableNode}
-          directTableChild={this.isDirectChildOfTableEnvironment}
-        />,
-        this.element
-      )
-    }
-    return this.element
+    ReactDOM.render(
+      <Tabular
+        view={view}
+        tabularNode={this.tabularNode}
+        parsedTableData={this.parsedTableData}
+        tableNode={this.tableNode}
+        directTableChild={this.isDirectChildOfTableEnvironment}
+      />,
+      element
+    )
+    return element
   }
 
   eq(widget: TabularWidget): boolean {
@@ -79,31 +39,27 @@ export class TabularWidget extends WidgetType {
       this.tabularNode.from === widget.tabularNode.from &&
       this.tableNode?.from === widget.tableNode?.from &&
       this.tableNode?.to === widget.tableNode?.to &&
-      this.content === widget.content
+      this.content === widget.content &&
+      this.isDirectChildOfTableEnvironment ===
+        widget.isDirectChildOfTableEnvironment
     )
   }
 
-  updateDOM(dom: HTMLElement, view: EditorView): boolean {
-    if (!this.parseResult) {
-      return false
-    }
-    this.element = dom
+  updateDOM(element: HTMLElement, view: EditorView): boolean {
     ReactDOM.render(
       <Tabular
         view={view}
         tabularNode={this.tabularNode}
-        parsedTableData={this.parseResult}
+        parsedTableData={this.parsedTableData}
         tableNode={this.tableNode}
         directTableChild={this.isDirectChildOfTableEnvironment}
       />,
-      this.element
+      element
     )
     return true
   }
 
-  destroy() {
-    if (this.element) {
-      ReactDOM.unmountComponentAtNode(this.element)
-    }
+  destroy(element: HTMLElement) {
+    ReactDOM.unmountComponentAtNode(element)
   }
 }