mirror of
https://github.com/BookStackApp/BookStack.git
synced 2024-12-14 07:07:21 +08:00
8a13a9df80
Added safeguarding/matching of source/target sizes to prevent broken tables.
97 lines
3.3 KiB
TypeScript
97 lines
3.3 KiB
TypeScript
import {NodeClipboard} from "./node-clipboard";
|
|
import {CustomTableRowNode} from "../nodes/custom-table-row";
|
|
import {$getTableFromSelection, $getTableRowsFromSelection} from "./tables";
|
|
import {$getSelection, LexicalEditor} from "lexical";
|
|
import {$createCustomTableCellNode, $isCustomTableCellNode} from "../nodes/custom-table-cell";
|
|
import {CustomTableNode} from "../nodes/custom-table";
|
|
import {TableMap} from "./table-map";
|
|
|
|
const rowClipboard: NodeClipboard<CustomTableRowNode> = new NodeClipboard<CustomTableRowNode>(CustomTableRowNode);
|
|
|
|
export function isRowClipboardEmpty(): boolean {
|
|
return rowClipboard.size() === 0;
|
|
}
|
|
|
|
export function validateRowsToCopy(rows: CustomTableRowNode[]): void {
|
|
let commonRowSize: number|null = null;
|
|
|
|
for (const row of rows) {
|
|
const cells = row.getChildren().filter(n => $isCustomTableCellNode(n));
|
|
let rowSize = 0;
|
|
for (const cell of cells) {
|
|
rowSize += cell.getColSpan() || 1;
|
|
if (cell.getRowSpan() > 1) {
|
|
throw Error('Cannot copy rows with merged cells');
|
|
}
|
|
}
|
|
|
|
if (commonRowSize === null) {
|
|
commonRowSize = rowSize;
|
|
} else if (commonRowSize !== rowSize) {
|
|
throw Error('Cannot copy rows with inconsistent sizes');
|
|
}
|
|
}
|
|
}
|
|
|
|
export function validateRowsToPaste(rows: CustomTableRowNode[], targetTable: CustomTableNode): void {
|
|
const tableColCount = (new TableMap(targetTable)).columnCount;
|
|
for (const row of rows) {
|
|
const cells = row.getChildren().filter(n => $isCustomTableCellNode(n));
|
|
let rowSize = 0;
|
|
for (const cell of cells) {
|
|
rowSize += cell.getColSpan() || 1;
|
|
}
|
|
|
|
if (rowSize > tableColCount) {
|
|
throw Error('Cannot paste rows that are wider than target table');
|
|
}
|
|
|
|
while (rowSize < tableColCount) {
|
|
row.append($createCustomTableCellNode());
|
|
rowSize++;
|
|
}
|
|
}
|
|
}
|
|
|
|
export function $cutSelectedRowsToClipboard(): void {
|
|
const rows = $getTableRowsFromSelection($getSelection());
|
|
validateRowsToCopy(rows);
|
|
rowClipboard.set(...rows);
|
|
for (const row of rows) {
|
|
row.remove();
|
|
}
|
|
}
|
|
|
|
export function $copySelectedRowsToClipboard(): void {
|
|
const rows = $getTableRowsFromSelection($getSelection());
|
|
validateRowsToCopy(rows);
|
|
rowClipboard.set(...rows);
|
|
}
|
|
|
|
export function $pasteClipboardRowsBefore(editor: LexicalEditor): void {
|
|
const selection = $getSelection();
|
|
const rows = $getTableRowsFromSelection(selection);
|
|
const table = $getTableFromSelection(selection);
|
|
const lastRow = rows[rows.length - 1];
|
|
if (lastRow && table) {
|
|
const clipboardRows = rowClipboard.get(editor);
|
|
validateRowsToPaste(clipboardRows, table);
|
|
for (const row of clipboardRows) {
|
|
lastRow.insertBefore(row);
|
|
}
|
|
}
|
|
}
|
|
|
|
export function $pasteRowsAfter(editor: LexicalEditor): void {
|
|
const selection = $getSelection();
|
|
const rows = $getTableRowsFromSelection(selection);
|
|
const table = $getTableFromSelection(selection);
|
|
const lastRow = rows[rows.length - 1];
|
|
if (lastRow && table) {
|
|
const clipboardRows = rowClipboard.get(editor).reverse();
|
|
validateRowsToPaste(clipboardRows, table);
|
|
for (const row of clipboardRows) {
|
|
lastRow.insertAfter(row);
|
|
}
|
|
}
|
|
} |