Преглед изворни кода

Merge pull request #14248 from overleaf/mj-table-cell-alignment

[visual] Enable cell alignment in table generator

GitOrigin-RevId: 31be50712fd6b3b1da37b0906965f70618f6a9b3
Mathias Jakobsen пре 3 година
родитељ
комит
31c285871a

+ 1 - 0
services/web/frontend/js/features/source-editor/components/table-generator/tabular.tsx

@@ -34,6 +34,7 @@ export type ColumnDefinition = {
   alignment: 'left' | 'center' | 'right' | 'paragraph'
   alignment: 'left' | 'center' | 'right' | 'paragraph'
   borderLeft: number
   borderLeft: number
   borderRight: number
   borderRight: number
+  content: string
 }
 }
 
 
 export type TableData = {
 export type TableData = {

+ 46 - 2
services/web/frontend/js/features/source-editor/components/table-generator/toolbar/commands.ts

@@ -1,7 +1,8 @@
 import { EditorView } from '@codemirror/view'
 import { EditorView } from '@codemirror/view'
-import { Positions } from '../tabular'
+import { ColumnDefinition, Positions } from '../tabular'
 import { ChangeSpec } from '@codemirror/state'
 import { ChangeSpec } from '@codemirror/state'
-import { RowSeparator } from '../utils'
+import { RowSeparator, parseColumnSpecifications } from '../utils'
+import { TableSelection } from '../contexts/selection-context'
 
 
 /* eslint-disable no-unused-vars */
 /* eslint-disable no-unused-vars */
 export enum BorderTheme {
 export enum BorderTheme {
@@ -96,3 +97,46 @@ export const setBorders = (
     })
     })
   }
   }
 }
 }
+
+export const setAlignment = (
+  view: EditorView,
+  selection: TableSelection,
+  alignment: 'left' | 'right' | 'center',
+  positions: Positions
+) => {
+  const specification = view.state.sliceDoc(
+    positions.columnDeclarations.from,
+    positions.columnDeclarations.to
+  )
+  const columnSpecification = parseColumnSpecifications(specification)
+  const { minX, maxX } = selection.normalized()
+  for (let i = minX; i <= maxX; i++) {
+    if (selection.isColumnSelected(i, positions.rowPositions.length)) {
+      if (columnSpecification[i].alignment === alignment) {
+        continue
+      }
+      columnSpecification[i].alignment = alignment
+      // TODO: This won't work for paragraph, which needs width argument
+      columnSpecification[i].content = alignment[0]
+    }
+  }
+  const newSpecification = generateColumnSpecification(columnSpecification)
+  view.dispatch({
+    changes: [
+      {
+        from: positions.columnDeclarations.from,
+        to: positions.columnDeclarations.to,
+        insert: newSpecification,
+      },
+    ],
+  })
+}
+
+const generateColumnSpecification = (columns: ColumnDefinition[]) => {
+  return columns
+    .map(
+      ({ borderLeft, borderRight, content }) =>
+        `${'|'.repeat(borderLeft)}${content}${'|'.repeat(borderRight)}`
+    )
+    .join('')
+}

+ 16 - 7
services/web/frontend/js/features/source-editor/components/table-generator/toolbar/toolbar.tsx

@@ -4,7 +4,7 @@ import { ToolbarButton } from './toolbar-button'
 import { ToolbarButtonMenu } from './toolbar-button-menu'
 import { ToolbarButtonMenu } from './toolbar-button-menu'
 import { ToolbarDropdown } from './toolbar-dropdown'
 import { ToolbarDropdown } from './toolbar-dropdown'
 import MaterialIcon from '../../../../../shared/components/material-icon'
 import MaterialIcon from '../../../../../shared/components/material-icon'
-import { BorderTheme, setBorders } from './commands'
+import { BorderTheme, setAlignment, setBorders } from './commands'
 import { useCodeMirrorViewContext } from '../../codemirror-editor'
 import { useCodeMirrorViewContext } from '../../codemirror-editor'
 import { useTableContext } from '../contexts/table-context'
 import { useTableContext } from '../contexts/table-context'
 
 
@@ -87,27 +87,36 @@ export const Toolbar = memo(function Toolbar() {
           label="Alignment"
           label="Alignment"
           icon="format_align_left"
           icon="format_align_left"
           id="table-generator-align-dropdown"
           id="table-generator-align-dropdown"
-          disabled
+          disabled={
+            !selection.isColumnSelected(
+              selection.from.cell,
+              positions.rowPositions.length
+            )
+          }
         >
         >
           <ToolbarButton
           <ToolbarButton
             icon="format_align_left"
             icon="format_align_left"
             id="table-generator-align-left"
             id="table-generator-align-left"
             label="Left"
             label="Left"
+            command={() => {
+              setAlignment(view, selection, 'left', positions)
+            }}
           />
           />
           <ToolbarButton
           <ToolbarButton
             icon="format_align_center"
             icon="format_align_center"
             id="table-generator-align-center"
             id="table-generator-align-center"
             label="Center"
             label="Center"
+            command={() => {
+              setAlignment(view, selection, 'center', positions)
+            }}
           />
           />
           <ToolbarButton
           <ToolbarButton
             icon="format_align_right"
             icon="format_align_right"
             id="table-generator-align-right"
             id="table-generator-align-right"
             label="Right"
             label="Right"
-          />
-          <ToolbarButton
-            icon="format_align_justify"
-            id="table-generator-align-justify"
-            label="Justify"
+            command={() => {
+              setAlignment(view, selection, 'right', positions)
+            }}
           />
           />
         </ToolbarButtonMenu>
         </ToolbarButtonMenu>
         <ToolbarButton
         <ToolbarButton

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

@@ -11,21 +11,26 @@ export type RowPosition = {
   hlines: { from: number; to: number }[]
   hlines: { from: number; to: number }[]
 }
 }
 
 
-function parseColumnSpecifications(specification: string): ColumnDefinition[] {
+export function parseColumnSpecifications(
+  specification: string
+): ColumnDefinition[] {
   const columns: ColumnDefinition[] = []
   const columns: ColumnDefinition[] = []
   let currentAlignment: ColumnDefinition['alignment'] | undefined
   let currentAlignment: ColumnDefinition['alignment'] | undefined
   let currentBorderLeft = 0
   let currentBorderLeft = 0
   let currentBorderRight = 0
   let currentBorderRight = 0
+  let currentContent = ''
   function maybeCommit() {
   function maybeCommit() {
     if (currentAlignment !== undefined) {
     if (currentAlignment !== undefined) {
       columns.push({
       columns.push({
         alignment: currentAlignment,
         alignment: currentAlignment,
         borderLeft: currentBorderLeft,
         borderLeft: currentBorderLeft,
         borderRight: currentBorderRight,
         borderRight: currentBorderRight,
+        content: currentContent,
       })
       })
       currentAlignment = undefined
       currentAlignment = undefined
       currentBorderLeft = 0
       currentBorderLeft = 0
       currentBorderRight = 0
       currentBorderRight = 0
+      currentContent = ''
     }
     }
   }
   }
   for (let i = 0; i < specification.length; i++) {
   for (let i = 0; i < specification.length; i++) {
@@ -45,18 +50,23 @@ function parseColumnSpecifications(specification: string): ColumnDefinition[] {
       }
       }
       case 'c':
       case 'c':
         currentAlignment = 'center'
         currentAlignment = 'center'
+        currentContent += 'c'
         break
         break
       case 'l':
       case 'l':
         currentAlignment = 'left'
         currentAlignment = 'left'
+        currentContent += 'l'
         break
         break
       case 'r':
       case 'r':
         currentAlignment = 'right'
         currentAlignment = 'right'
+        currentContent += 'r'
         break
         break
       case 'p': {
       case 'p': {
         currentAlignment = 'paragraph'
         currentAlignment = 'paragraph'
+        currentContent += 'p'
         // TODO: Parse these details
         // TODO: Parse these details
         while (i < specification.length && specification.charAt(i) !== '}') {
         while (i < specification.length && specification.charAt(i) !== '}') {
           i++
           i++
+          currentContent += specification.charAt(i)
         }
         }
         break
         break
       }
       }