From 78a84e61bdb69fe98ddb42e8beb9b57d036b3b10 Mon Sep 17 00:00:00 2001 From: jocs Date: Sat, 16 Nov 2024 20:53:20 +0800 Subject: [PATCH] feat: selection across pages --- .../services/selection/convert-rect-range.ts | 132 +++++++++++++----- .../src/components/docs/layout/model/page.ts | 5 + 2 files changed, 105 insertions(+), 32 deletions(-) diff --git a/packages/docs-ui/src/services/selection/convert-rect-range.ts b/packages/docs-ui/src/services/selection/convert-rect-range.ts index 3490cab6f89..81050ebb3da 100644 --- a/packages/docs-ui/src/services/selection/convert-rect-range.ts +++ b/packages/docs-ui/src/services/selection/convert-rect-range.ts @@ -104,7 +104,12 @@ export function compareNodePositionInTable(a: INodePosition, b: INodePosition): } function firstGlyphInCellPage(cellPage: IDocumentSkeletonPage): Nullable { - return cellPage.sections[0].columns[0].lines[0].divides[0].glyphGroup[0]; + const lines = cellPage.sections[0].columns[0].lines; + if (lines.length === 0) { + return; + } + + return lines[0].divides[0].glyphGroup[0]; } function lastGlyphInCellPage(cellPage: IDocumentSkeletonPage): Nullable { @@ -112,12 +117,59 @@ function lastGlyphInCellPage(cellPage: IDocumentSkeletonPage): Nullable { + let s = startCol; + let e = endCol; + let startCell = cells[s]; + let endCell = cells[e]; + + while (s < e && (isEmptyCellPage(startCell) || isEmptyCellPage(endCell))) { + if (isEmptyCellPage(startCell)) { + s++; + startCell = cells[s]; + } else if (isEmptyCellPage(endCell)) { + e--; + endCell = cells[e]; + } + } + + if (!isEmptyCellPage(startCell) && !isEmptyCellPage(endCell)) { + return [startCell, endCell]; + } +} + +function isAllSpanRows(positions: IRectRangeNodePositions[], cols: number) { + return positions.every((position) => { + const { anchor, focus } = position; + const { path: anchorPath } = anchor; + const { path: focusPath } = focus; + + const anchorCol = anchorPath[anchorPath.length - 1] as number; + const focusCol = focusPath[focusPath.length - 1] as number; + + return Math.abs(anchorCol - focusCol) === cols - 1; + }); +} + interface IRectRangeNodePositions { anchor: INodePosition; focus: INodePosition; @@ -236,13 +288,17 @@ export class NodePositionConvertToRectRange { focusNodePosition: INodePosition ): Nullable { const nodePositionGroup: IRectRangeNodePositions[] = []; - const compare = compareNodePositionInTable(anchorNodePosition, focusNodePosition); - const startNodePosition = compare ? anchorNodePosition : focusNodePosition; - const endNodePosition = compare ? focusNodePosition : anchorNodePosition; + const anchorIndex = this._docSkeleton.findCharIndexByPosition(anchorNodePosition); + const focusIndex = this._docSkeleton.findCharIndexByPosition(focusNodePosition); + + if (anchorIndex == null || focusIndex == null) { + return; + } + + const compare = anchorIndex < focusIndex; // Start segmentPage will equal to end segmentPage. - const { segmentPage } = startNodePosition; - const rectInfo = this._getTableRectRangeInfo(startNodePosition, endNodePosition); + const rectInfo = this._getTableRectRangeInfo(anchorNodePosition, focusNodePosition); if (rectInfo == null) { return; @@ -268,13 +324,13 @@ export class NodePositionConvertToRectRange { } for (const table of tables) { - this._collectPositionGroup(table, nodePositionGroup, startRowIndex, endRowIndex, startColumnIndex, endColumnIndex, segmentPage, compare); + this._collectPositionGroup(table, nodePositionGroup, startRowIndex, endRowIndex, startColumnIndex, endColumnIndex, anchorNodePosition, focusNodePosition, compare); } const totalColumns = tables[0].rows[0].cells.length; // Span entires row. - if (startColumnIndex === 0 && endColumnIndex === totalColumns - 1) { + if (isAllSpanRows(nodePositionGroup, totalColumns)) { const firstPosition = nodePositionGroup[0]; const lastPosition = nodePositionGroup[nodePositionGroup.length - 1]; @@ -296,7 +352,8 @@ export class NodePositionConvertToRectRange { endRowIndex: number, startColumnIndex: number, endColumnIndex: number, - segmentPage: number, + anchorPosition: INodePosition, + focusPosition: INodePosition, compare: boolean ) { // Not span entires row. @@ -311,18 +368,25 @@ export class NodePositionConvertToRectRange { break; } - const startCellInRow = row.cells[startColumnIndex]; - const endCellInRow = row.cells[endColumnIndex]; + const cells = findNonEmptyCellPages(row.cells, startColumnIndex, endColumnIndex); + + if (cells == null) { + continue; + } + const [startCell, endCell] = cells; - const startCellGlyph = firstGlyphInCellPage(startCellInRow); - const endCellGlyph = lastGlyphInCellPage(endCellInRow); + const startCellGlyph = firstGlyphInCellPage(startCell); + const endCellGlyph = lastGlyphInCellPage(endCell); if (startCellGlyph == null || endCellGlyph == null) { continue; } - const startPosition = this._docSkeleton.findPositionByGlyph(startCellGlyph, segmentPage); - const endPosition = this._docSkeleton.findPositionByGlyph(endCellGlyph, segmentPage); + const startSegmentPage = compare ? anchorPosition.segmentPage : focusPosition.segmentPage; + const endSegmentPage = compare ? focusPosition.segmentPage : anchorPosition.segmentPage; + + const startPosition = this._docSkeleton.findPositionByGlyph(startCellGlyph, startSegmentPage); + const endPosition = this._docSkeleton.findPositionByGlyph(endCellGlyph, endSegmentPage); if (startPosition == null || endPosition == null) { continue; @@ -344,7 +408,7 @@ export class NodePositionConvertToRectRange { } } - private _getTableRectRangeInfo(startNodePosition: INodePosition, endNodePosition: INodePosition) { + private _getTableRectRangeInfo(anchorPosition: INodePosition, focusPosition: INodePosition) { const docSkeleton = this._docSkeleton; const skeletonData = docSkeleton.getSkeletonData(); @@ -354,31 +418,35 @@ export class NodePositionConvertToRectRange { const { pages } = skeletonData; - const { path: startPath } = startNodePosition; - const { path: endPath } = endNodePosition; - const startCell = getPageFromPath(skeletonData, startPath); - const endCell = getPageFromPath(skeletonData, endPath); + const { path: anchorPath } = anchorPosition; + const { path: focusPath } = focusPosition; + const anchorCell = getPageFromPath(skeletonData, anchorPath); + const focusCell = getPageFromPath(skeletonData, focusPath); - if (startCell == null || endCell == null) { + if (anchorCell == null || focusCell == null) { return; } - const tableId = startCell.segmentId; - const startRow = (startCell.parent as IDocumentSkeletonRow).index; - const startColumn = (startCell.parent as IDocumentSkeletonRow).cells.indexOf(startCell); + const tableId = anchorCell.segmentId; + const anchorRow = (anchorCell.parent as IDocumentSkeletonRow).index; + const anchorColumn = (anchorCell.parent as IDocumentSkeletonRow).cells.indexOf(anchorCell); + + const focusRow = (focusCell?.parent as IDocumentSkeletonRow).index; + const focusColumn = (focusCell?.parent as IDocumentSkeletonRow).cells.indexOf(focusCell); + + const startRowIndex = Math.min(anchorRow, focusRow); + const endRowIndex = Math.max(anchorRow, focusRow); - const endRow = (endCell?.parent as IDocumentSkeletonRow).index; - const endColumn = (endCell?.parent as IDocumentSkeletonRow).cells.indexOf(endCell); + const startColumnIndex = Math.min(anchorColumn, focusColumn); + const endColumnIndex = Math.max(anchorColumn, focusColumn); return { pages, tableId, - startCell, - endCell, - startRowIndex: startRow, - startColumnIndex: startColumn, - endRowIndex: endRow, - endColumnIndex: endColumn, + startRowIndex, + startColumnIndex, + endRowIndex, + endColumnIndex, }; } } diff --git a/packages/engine-render/src/components/docs/layout/model/page.ts b/packages/engine-render/src/components/docs/layout/model/page.ts index 7f1288b70ae..f421812e1c7 100644 --- a/packages/engine-render/src/components/docs/layout/model/page.ts +++ b/packages/engine-render/src/components/docs/layout/model/page.ts @@ -370,6 +370,11 @@ export function createSkeletonCellPages( cellSectionBreakConfig ); + for (const p of pages) { + p.type = DocumentSkeletonPageType.CELL; + p.segmentId = tableConfig.tableId; + } + updateBlockIndex(pages, cellNode.startIndex); return pages;