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

Merge pull request #14630 from overleaf/mj-table-copy-paste

[visual] Fix copying and pasting with multicolumn

GitOrigin-RevId: 4710cedb40493ed018daf122e68aecfb91e1efc4
Mathias Jakobsen 2 лет назад
Родитель
Сommit
5d33e5a658

+ 27 - 25
services/web/frontend/js/features/source-editor/components/table-generator/table.tsx

@@ -262,24 +262,26 @@ export const Table: FC = () => {
       const changes: ChangeSpec[] = []
       const data = event.clipboardData?.getData('text/plain')
       if (data) {
-        const rows = data.split('\n')
-        const { minX, minY } = selection.normalized()
-        for (let row = 0; row < rows.length; row++) {
-          if (tableData.rows.length <= minY + row) {
-            // TODO: Add rows
-            continue
+        const cells = data.split('\n').map(row => row.split('\t'))
+        const { minY, minX } = selection.normalized()
+        for (let row = 0; row < cells.length; ++row) {
+          const rowIndex = minY + row
+          if (rowIndex >= tableData.rows.length) {
+            // TODO: add more rows
+            break
           }
-          const cells = rows[row].split('\t')
-          for (let cell = 0; cell < cells.length; cell++) {
-            if (tableData.columns.length <= minX + cell) {
-              // TODO: Add columns
-              continue
+          const cellStart = tableData.getCellIndex(rowIndex, minX)
+          for (let column = 0; column < cells[row].length; ++column) {
+            const cellIndex = cellStart + column
+            if (cellIndex >= tableData.rows[rowIndex].cells.length) {
+              // TODO: add more columns
+              break
             }
-            const cellData = tableData.getCell(minY + row, minX + cell)
+            const cell = tableData.rows[rowIndex].cells[cellIndex]
             changes.push({
-              from: cellData.from,
-              to: cellData.to,
-              insert: cells[cell],
+              from: cell.from,
+              to: cell.to,
+              insert: cells[row][column],
             })
           }
         }
@@ -293,16 +295,16 @@ export const Table: FC = () => {
         return false
       }
       event.preventDefault()
-      const { minX, minY, maxX, maxY } = selection.normalized()
-      const content = []
-      for (let row = minY; row <= maxY; row++) {
-        const rowContent = []
-        for (let cell = minX; cell <= maxX; cell++) {
-          rowContent.push(tableData.getCell(row, cell).content)
-        }
-        content.push(rowContent.join('\t'))
-      }
-      navigator.clipboard.writeText(content.join('\n'))
+      const { minY, maxY } = selection.normalized()
+      const cells: string[][] = Array.from(
+        { length: maxY - minY + 1 },
+        () => []
+      )
+      tableData.iterateSelection(selection, (cell, row) => {
+        cells[row - minY].push(cell.content)
+      })
+      const content = cells.map(row => row.join('\t')).join('\n')
+      navigator.clipboard.writeText(content)
     }
     window.addEventListener('paste', onPaste)
     window.addEventListener('copy', onCopy)

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

@@ -5,6 +5,7 @@ import { Toolbar } from './toolbar/toolbar'
 import { Table } from './table'
 import {
   SelectionContextProvider,
+  TableSelection,
   useSelectionContext,
 } from './contexts/selection-context'
 import {
@@ -78,6 +79,34 @@ export class TableData {
     return this.rows[row].cells.length - 1
   }
 
+  iterateCells(
+    minRow: number,
+    maxRow: number,
+    minColumn: number,
+    maxColumn: number,
+    callback: (cell: CellData, row: number, column: number) => void
+  ) {
+    for (let row = minRow; row <= maxRow; ++row) {
+      let currentCellOffset = this.getCellBoundaries(row, minColumn).from
+      const minX = this.getCellIndex(row, minColumn)
+      const maxX = this.getCellIndex(row, maxColumn)
+      for (let column = minX; column <= maxX; ++column) {
+        const currentCell = this.rows[row].cells[column]
+        const skip = currentCell.multiColumn?.columnSpan ?? 1
+        callback(currentCell, row, currentCellOffset)
+        currentCellOffset += skip
+      }
+    }
+  }
+
+  iterateSelection(
+    selection: TableSelection,
+    callback: (cell: CellData, row: number, column: number) => void
+  ) {
+    const { minX, maxX, minY, maxY } = selection.normalized()
+    this.iterateCells(minY, maxY, minX, maxX, callback)
+  }
+
   getCell(row: number, column: number): CellData {
     return this.rows[row].cells[this.getCellIndex(row, column)]
   }