diff --git a/.changeset/empty-needles-visit.md b/.changeset/empty-needles-visit.md new file mode 100644 index 000000000..0507aff52 --- /dev/null +++ b/.changeset/empty-needles-visit.md @@ -0,0 +1,5 @@ +--- +'@plait/draw': minor +--- + +support bind element to table diff --git a/packages/draw/src/interfaces/geometry.ts b/packages/draw/src/interfaces/geometry.ts index a5cdfb17a..134fc9af8 100644 --- a/packages/draw/src/interfaces/geometry.ts +++ b/packages/draw/src/interfaces/geometry.ts @@ -76,15 +76,15 @@ export interface PlaitCommonGeometry extends PlaitElement { points: [Point, Point]; type: 'geometry'; shape: GeometryShapes; - + angle: number; + opacity: number; // node style attributes fill?: string; strokeColor?: string; strokeWidth?: number; strokeStyle?: StrokeStyle; - - angle: number; - opacity: number; + // end + tableId?: string; } export interface PlaitMultipleTextGeometry extends PlaitCommonGeometry { diff --git a/packages/draw/src/interfaces/image.ts b/packages/draw/src/interfaces/image.ts index d8b3c84e4..6645b6701 100644 --- a/packages/draw/src/interfaces/image.ts +++ b/packages/draw/src/interfaces/image.ts @@ -4,6 +4,7 @@ export interface PlaitCommonImage extends PlaitElement { points: [Point, Point]; type: 'image'; angle: number; + tableId?: string; } export interface PlaitImage extends PlaitCommonImage { diff --git a/packages/draw/src/interfaces/index.ts b/packages/draw/src/interfaces/index.ts index 0fbced3de..c99a94ef3 100644 --- a/packages/draw/src/interfaces/index.ts +++ b/packages/draw/src/interfaces/index.ts @@ -1,5 +1,3 @@ -import { ParagraphElement } from '@plait/text'; -import { EngineExtraData } from './engine'; import { BasicShapes, FlowchartSymbols, GeometryShapes, PlaitGeometry, TableSymbols, UMLSymbols } from './geometry'; import { PlaitImage } from './image'; import { PlaitLine } from './line'; diff --git a/packages/draw/src/interfaces/line.ts b/packages/draw/src/interfaces/line.ts index 9b8ceb43c..fc6c5af4a 100644 --- a/packages/draw/src/interfaces/line.ts +++ b/packages/draw/src/interfaces/line.ts @@ -60,18 +60,16 @@ export interface PlaitLine extends PlaitElement { type: 'line'; shape: LineShape; points: Point[]; - source: LineHandle; target: LineHandle; - texts: LineText[]; - + opacity: number; // node style attributes strokeColor?: string; strokeWidth?: number; strokeStyle?: StrokeStyle; - - opacity: number; + // end + tableId?: string; } export interface PlaitStraightLine extends PlaitLine { diff --git a/packages/draw/src/plugins/with-table.ts b/packages/draw/src/plugins/with-table.ts index b5bd52cb9..71900b282 100644 --- a/packages/draw/src/plugins/with-table.ts +++ b/packages/draw/src/plugins/with-table.ts @@ -10,12 +10,27 @@ import { getSelectedElements, toViewBoxPoint, toHostPoint, - getHitElementByPoint + getHitElementByPoint, + getMovingElements, + isMovingElements, + InsertNodeOperation } from '@plait/core'; -import { editCell, getHitCell } from '../utils/table'; +import { editCell, getHitCell, getRelatedElementsInTable, setElementsTableId } from '../utils/table'; +import { PlaitDrawElement } from '../interfaces'; export const withTable = (board: PlaitBoard) => { - const { drawElement, getRectangle, isRectangleHit, isHit, isMovable, getDeletedFragment, dblClick } = board; + const { + drawElement, + getRectangle, + isRectangleHit, + isHit, + isMovable, + getDeletedFragment, + dblClick, + getRelatedFragment, + onChange, + pointerMove + } = board; board.drawElement = (context: PlaitPluginElementContext) => { if (PlaitTableElement.isTable(context.element)) { return TableComponent; @@ -55,6 +70,12 @@ export const withTable = (board: PlaitBoard) => { return isMovable(element); }; + board.getRelatedFragment = (elements: PlaitElement[], originData?: PlaitElement[]) => { + const selectedElements = originData?.length ? originData : getSelectedElements(board); + const elementsInTable = getRelatedElementsInTable(board, selectedElements); + return getRelatedFragment([...elements, ...elementsInTable], originData); + }; + board.isRectangleHit = (element: PlaitElement, selection: Selection) => { if (PlaitTableElement.isTable(element)) { const rangeRectangle = RectangleClient.getRectangleByPoints([selection.anchor, selection.focus]); @@ -77,6 +98,22 @@ export const withTable = (board: PlaitBoard) => { } dblClick(event); }; - + + board.pointerMove = (event: PointerEvent) => { + pointerMove(event); + if (isMovingElements(board)) { + const movingDrawElements = getMovingElements(board).filter(item => PlaitDrawElement.isDrawElement(item)) as PlaitDrawElement[]; + setElementsTableId(board, movingDrawElements); + } + }; + + board.onChange = () => { + onChange(); + const insertNodes = board.operations + .filter(op => op.type === 'insert_node' && PlaitDrawElement.isDrawElement(op.node)) + .map(item => (item as InsertNodeOperation).node) as PlaitDrawElement[]; + setElementsTableId(board, insertNodes); + }; + return board; }; diff --git a/packages/draw/src/table.component.ts b/packages/draw/src/table.component.ts index 3d0889e5b..e87d80cb8 100644 --- a/packages/draw/src/table.component.ts +++ b/packages/draw/src/table.component.ts @@ -1,5 +1,12 @@ -import { ChangeDetectionStrategy, Component, OnDestroy, OnInit } from '@angular/core'; -import { PlaitBoard, PlaitPluginElementContext, OnContextChanged, ACTIVE_STROKE_WIDTH, RectangleClient } from '@plait/core'; +import { ChangeDetectionStrategy, Component, DestroyRef, inject, OnDestroy, OnInit } from '@angular/core'; +import { + PlaitBoard, + PlaitPluginElementContext, + OnContextChanged, + ACTIVE_STROKE_WIDTH, + RectangleClient, + PlaitContextService +} from '@plait/core'; import { ActiveGenerator, canResize, CommonPluginElement } from '@plait/common'; import { PlaitTable, PlaitTableCell } from './interfaces/table'; import { PlaitDrawShapeText, TextGenerator } from './generators/text.generator'; @@ -23,7 +30,9 @@ export class TableComponent extends CommonPluginElement textGenerator!: TextGenerator; - constructor() { + contextService = inject(PlaitContextService); + + constructor(private destroyRef: DestroyRef) { super(); } diff --git a/packages/draw/src/utils/table.ts b/packages/draw/src/utils/table.ts index 89ac8494d..5d0e2fbc2 100644 --- a/packages/draw/src/utils/table.ts +++ b/packages/draw/src/utils/table.ts @@ -1,6 +1,7 @@ -import { Point, RectangleClient } from '@plait/core'; -import { PlaitTable, PlaitTableCell, PlaitTableCellWithPoints } from '../interfaces/table'; +import { findElements, PlaitBoard, PlaitElement, Point, RectangleClient, Transforms } from '@plait/core'; +import { PlaitTable, PlaitTableCell, PlaitTableCellWithPoints, PlaitTableElement } from '../interfaces/table'; import { getTextManage } from '../generators/text.generator'; +import { PlaitDrawElement } from '../interfaces'; export function getCellsWithPoints(table: PlaitTable): PlaitTableCellWithPoints[] { const rectangle = RectangleClient.getRectangleByPoints(table.points); @@ -97,3 +98,75 @@ export function editCell(cell: PlaitTableCell) { export function getTextManageByCell(cell: PlaitTableCell) { return getTextManage(cell.id); } + +export function getHitTableElement(tableElements: PlaitTable[], points: Point[]) { + return tableElements.find(item => { + const tableRectangle = RectangleClient.getRectangleByPoints(item.points); + const centerPoint = RectangleClient.getCenterPointByPoints(points); + return RectangleClient.isPointInRectangle(tableRectangle, centerPoint); + }); +} + +export function getTableElementByCell(board: PlaitBoard, cell: PlaitTableCell): PlaitTable { + return board.children.find( + element => + PlaitTableElement.isTable(element) && + element.rows.map(row => row.id).includes(cell.rowId) && + element.columns.map(column => column.id).includes(cell.columnId) + ) as PlaitTable; +} + +export function setElementsTableId(board: PlaitBoard, elements: PlaitDrawElement[]) { + const tableElements = findElements(board, { + match: element => PlaitTableElement.isTable(element), + recursion: () => false + }) as PlaitTable[]; + if (tableElements.length && elements.length) { + elements.forEach(element => { + const hitTable = getHitTableElement(tableElements, element.points); + if (!hitTable && element.tableId) { + const path = PlaitBoard.findPath(board, element); + Transforms.setNode(board, { tableId: undefined }, path); + } + if (hitTable && element.tableId !== hitTable.id) { + const path = PlaitBoard.findPath(board, element); + Transforms.setNode(board, { tableId: hitTable.id }, path); + } + }); + } +} + +export function getRelatedElementsInTable(board: PlaitBoard, elements: PlaitElement[]): PlaitDrawElement[] { + const tableIds = elements.filter(item => PlaitTableElement.isTable(item)).map(item => item.id); + return board.children.filter(item => PlaitDrawElement.isDrawElement(item) && tableIds.includes(item.tableId)) as PlaitDrawElement[]; +} + +export function getRelatedElementsInCell(board: PlaitBoard, cell: PlaitTableCell): PlaitDrawElement[] { + const table = getTableElementByCell(board, cell); + if (table) { + const tableRelatedElements = findElements(board, { + match: item => PlaitDrawElement.isDrawElement(item) && item.tableId === table.id, + recursion: () => false + }); + if (tableRelatedElements.length) { + return tableRelatedElements.filter(element => { + const hitCell = getHitCellByCenterPoints(table, element.points!); + return hitCell && hitCell.id === cell.id; + }) as PlaitDrawElement[]; + } + } + return []; +} + +export function getHitCellByCenterPoints(table: PlaitTable, points: Point[]) { + const cells = getCellsWithPoints(table); + const rectangle = RectangleClient.getRectangleByPoints(points); + const cell = cells.find(item => { + const centerPoint = RectangleClient.getCenterPointByPoints(points); + return RectangleClient.isPointInRectangle(rectangle, centerPoint); + }); + if (cell) { + return table.cells.find(item => item.id === cell.id); + } + return null; +} \ No newline at end of file diff --git a/src/app/editor/mock-data.ts b/src/app/editor/mock-data.ts index 8e87eda00..7b19e1268 100644 --- a/src/app/editor/mock-data.ts +++ b/src/app/editor/mock-data.ts @@ -701,6 +701,50 @@ export const mockTableData: PlaitDrawElement[] = [ } } ] + }, + { + id: 'QPFwr', + type: 'geometry', + shape: 'ellipse', + angle: 0, + opacity: 1, + textHeight: 20, + text: { + children: [ + { + text: '' + } + ], + align: 'center' + }, + tableId: 'jhETT', + points: [ + [166.91015625, 4.05859375], + [251.8828125, 77.99609375] + ], + strokeWidth: 2 + }, + { + id: 'mWDfM', + type: 'geometry', + shape: 'rightArrow', + angle: 0, + opacity: 1, + textHeight: 20, + text: { + children: [ + { + text: '' + } + ], + align: 'center' + }, + tableId: 'TTjhE', + points: [ + [802.728515625, 15.724609375], + [982.013671875, 89.662109375] + ], + strokeWidth: 2 } ] as PlaitDrawElement[];