Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(core): adjust table resize #931

Open
wants to merge 4 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/forty-dogs-admire.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@plait/draw': patch
---

adjust table resize
9 changes: 7 additions & 2 deletions packages/draw/src/interfaces/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,12 @@ export const PlaitDrawElement = {
}
},
isShapeElement: (value: any): value is PlaitShapeElement => {
return PlaitDrawElement.isImage(value) || PlaitDrawElement.isGeometry(value) || PlaitDrawElement.isTable(value) || PlaitDrawElement.isSwimlane(value);
return (
PlaitDrawElement.isImage(value) ||
PlaitDrawElement.isGeometry(value) ||
PlaitDrawElement.isTable(value) ||
PlaitDrawElement.isSwimlane(value)
);
},
isBasicShape: (value: any) => {
return Object.keys(BasicShapes).includes(value.shape);
Expand Down Expand Up @@ -77,5 +82,5 @@ export const PlaitDrawElement = {
},
isElementByTable: (value: any): value is PlaitBaseTable => {
return PlaitDrawElement.isTable(value) || PlaitDrawElement.isSwimlane(value) || PlaitDrawElement.isGeometryByTable(value);
},
}
};
232 changes: 156 additions & 76 deletions packages/draw/src/plugins/with-table-resize.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { getHitElementByPoint, PlaitBoard, Point, RectangleClient, Transforms, isSelectedElement } from '@plait/core';
import { PlaitBaseTable, PlaitTableBoard, PlaitTableCellWithPoints } from '../interfaces/table';
import { PlaitBaseTable, PlaitTableBoard, PlaitTableCell, PlaitTableCellWithPoints } from '../interfaces/table';
import {
getIndexByResizeHandle,
isCornerHandle,
Expand All @@ -22,7 +22,7 @@ interface TableResizeOptions extends ResizeOptions {
cell: PlaitTableCellWithPoints;
}

const MIN_CELL_SIZE = 20;
const MIN_CELL_SIZE = 32;

export function withTableResize(board: PlaitTableBoard) {
let snapG: SVGGElement | null;
Expand All @@ -35,26 +35,10 @@ export function withTableResize(board: PlaitTableBoard) {
hitTest: (point: Point) => {
const hitElement = getHitElementByPoint(board, point);
if (hitElement && PlaitDrawElement.isElementByTable(hitElement)) {
let rectangle = board.getRectangle(hitElement) as RectangleClient;
let handleRef = getHitRectangleResizeHandleRef(board, rectangle, point, hitElement.angle);
if (handleRef) {
const selectElement = isSelectedElement(board, hitElement);
if (
(selectElement && isSingleSelectElementByTable(board)) ||
(!selectElement && !isCornerHandle(board, handleRef.handle))
) {
return {
element: hitElement,
handle: handleRef.handle,
cursorClass: handleRef.cursorClass,
rectangle
};
}
}
const cells = getCellsWithPoints(board, hitElement);
for (let i = 0; i < cells.length; i++) {
rectangle = RectangleClient.getRectangleByPoints(cells[i].points);
handleRef = getHitRectangleResizeHandleRef(board, rectangle, point, 0);
const rectangle = RectangleClient.getRectangleByPoints(cells[i].points);
const handleRef = getHitRectangleResizeHandleRef(board, rectangle, point, 0);
if (handleRef && !isCornerHandle(board, handleRef.handle)) {
return {
element: hitElement,
Expand All @@ -67,12 +51,29 @@ export function withTableResize(board: PlaitTableBoard) {
};
}
}
const rectangle = board.getRectangle(hitElement) as RectangleClient;
const handleRef = getHitRectangleResizeHandleRef(board, rectangle, point, hitElement.angle);
if (handleRef) {
const selectElement = isSelectedElement(board, hitElement);
if (
(selectElement && isSingleSelectElementByTable(board)) ||
(!selectElement && !isCornerHandle(board, handleRef.handle))
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

这个条件是啥,记得不

) {
return {
element: hitElement,
handle: handleRef.handle,
cursorClass: handleRef.cursorClass,
rectangle
};
}
}
}
return null;
},
onResize: (resizeRef: ResizeRef<PlaitBaseTable, ResizeHandle, TableResizeOptions>, resizeState: ResizeState) => {
snapG?.remove();
const path = PlaitBoard.findPath(board, resizeRef.element);

if (resizeRef.options?.cell && resizeRef.rectangle) {
const handleIndex = getIndexByResizeHandle(resizeRef.handle);
const { originPoint, handlePoint } = getResizeOriginPointAndHandlePoint(board, handleIndex, resizeRef.rectangle!);
Expand All @@ -82,67 +83,83 @@ export function withTableResize(board: PlaitTableBoard) {
const targetPoints = originPoints.map(p => {
return movePointByZoomAndOriginPoint(p, originPoint, xZoom, yZoom);
}) as [Point, Point];
const offsetX = targetPoints[1][0] - originPoints[1][0];
const offsetY = targetPoints[1][1] - originPoints[1][1];
const width = targetPoints[1][0] - targetPoints[0][0];
const height = targetPoints[1][1] - targetPoints[0][1];
if (offsetX !== 0 && width >= MIN_CELL_SIZE) {
const { columns, points } = updateColumns(resizeRef.element, resizeRef.options?.cell.columnId, width, offsetX);
Transforms.setNode(board, { columns, points }, path);
} else if (offsetY !== 0 && height >= MIN_CELL_SIZE) {
const { rows, points } = updateRows(resizeRef.element, resizeRef.options?.cell.rowId, height, offsetY);
Transforms.setNode(board, { rows, points }, path);
}
} else {
const isFromCorner = isCornerHandle(board, resizeRef.handle);
const isAspectRatio = resizeState.isShift;
const handleIndex = getIndexByResizeHandle(resizeRef.handle);
const { originPoint, handlePoint } = getResizeOriginPointAndHandlePoint(board, handleIndex, resizeRef.rectangle!);
const resizeSnapRefOptions = getSnapResizingRefOptions(
board,
resizeRef,
resizeState,
{
originPoint,
handlePoint
},
isAspectRatio,
isFromCorner
);
const resizeSnapRef = getSnapResizingRef(board, [resizeRef.element], resizeSnapRefOptions);
snapG = resizeSnapRef.snapG;
PlaitBoard.getElementActiveHost(board).append(snapG);
const points = resizeSnapRef.activePoints as [Point, Point];
const originPoints = resizeRef.element.points;

const originRect = RectangleClient.getRectangleByPoints(originPoints);
const targetRect = RectangleClient.getRectangleByPoints(points);
const targetRect = RectangleClient.getRectangleByPoints(targetPoints);
const offsetWidth = targetRect.width - originRect.width;
const offsetHeight = targetRect.height - originRect.height;
let columns = [...resizeRef.element.columns];
let rows = [...resizeRef.element.rows];
if (offsetWidth !== 0) {
columns = columns.map(item => {
if (item.width) {
return {
...item,
width: item.width + offsetWidth * (item.width / originRect.width)
};
}
return item;
});
const direction = getResizeCellDirection(handleIndex);
if (offsetWidth !== 0 && direction) {
const columnIndex = getResizeColumnOrRowIndex(resizeRef.element, resizeRef.options?.cell, direction, false);
let width = targetPoints[1][0] - targetPoints[0][0];
if (resizeRef.options?.cell.colspan && resizeRef.options?.cell.colspan !== 1) {
const columnWidth = getResizeColumnOrRowSize(board, resizeRef.element, columnIndex, false);
width = columnWidth + offsetWidth;
}
if (width >= MIN_CELL_SIZE) {
const { columns, points } = updateColumns(
resizeRef.element,
resizeRef.element.columns[columnIndex].id,
width,
offsetWidth,
direction
);
Transforms.setNode(board, { columns, points }, path);
}
}
if (offsetHeight !== 0) {
rows = rows.map(item => {
if (item.height) {
return {
...item,
height: item.height + offsetHeight * (item.height / originRect.height)
};
}
return item;
});
if (offsetHeight !== 0 && direction) {
const rowIndex = getResizeColumnOrRowIndex(resizeRef.element, resizeRef.options?.cell, direction, true);
let height = targetPoints[1][1] - targetPoints[0][1];
if (resizeRef.options?.cell.rowspan && resizeRef.options?.cell.rowspan !== 1) {
const rowHeight = getResizeColumnOrRowSize(board, resizeRef.element, rowIndex, true);
height = rowHeight + offsetHeight;
}
if (height >= MIN_CELL_SIZE) {
const { rows, points } = updateRows(
resizeRef.element,
resizeRef.element.rows[rowIndex].id,
height,
offsetHeight,
direction
);
Transforms.setNode(board, { rows, points }, path);
}
}
} else {
const isFromCorner = isCornerHandle(board, resizeRef.handle);
if (isFromCorner) {
const handleIndex = getIndexByResizeHandle(resizeRef.handle);
const { originPoint, handlePoint } = getResizeOriginPointAndHandlePoint(board, handleIndex, resizeRef.rectangle!);
const resizeSnapRefOptions = getSnapResizingRefOptions(
board,
resizeRef,
resizeState,
{
originPoint,
handlePoint
},
true,
true
);
const resizeSnapRef = getSnapResizingRef(board, [resizeRef.element], resizeSnapRefOptions);
snapG = resizeSnapRef.snapG;
PlaitBoard.getElementActiveHost(board).append(snapG);
const points = resizeSnapRef.activePoints as [Point, Point];
const originPoints = resizeRef.element.points;
const originRect = RectangleClient.getRectangleByPoints(originPoints);
const targetRect = RectangleClient.getRectangleByPoints(points);
const offsetWidth = targetRect.width - originRect.width;
const offsetHeight = targetRect.height - originRect.height;
let columns = [...resizeRef.element.columns];
let rows = [...resizeRef.element.rows];
if (offsetWidth !== 0) {
columns = calculateRowsOrColumns(columns, offsetWidth, originRect.width, false);
}
if (offsetHeight !== 0) {
rows = calculateRowsOrColumns(rows, offsetHeight, originRect.height, true);
}
Transforms.setNode(board, { points: normalizeShapePoints(points), rows, columns }, path);
}
Transforms.setNode(board, { points: normalizeShapePoints(points), columns, rows }, path);
}
},
afterResize: (resizeRef: ResizeRef<PlaitBaseTable, ResizeHandle, TableResizeOptions>) => {
Expand All @@ -155,3 +172,66 @@ export function withTableResize(board: PlaitTableBoard) {

return board;
}

function calculateRowsOrColumns(
data: { id: string; width?: number; height?: number }[],
offset: number,
originSize: number,
isRow: boolean
) {
const dimension = isRow ? 'height' : 'width';
return data.map(item => {
if (item[dimension]) {
const value = item[dimension]! + offset * (item[dimension]! / originSize);
return {
...item,
[dimension]: value
};
}
return item;
});
}

function getResizeCellDirection(handleIndex: number) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

start 和 end 概念的抽取需要讨论下

if ([Number(ResizeHandle.s), Number(ResizeHandle.e)].includes(handleIndex)) {
return 'end';
}
if ([Number(ResizeHandle.n), Number(ResizeHandle.w)].includes(handleIndex)) {
return 'start';
}
return undefined;
}

function getResizeColumnOrRowIndex(element: PlaitBaseTable, resizeCell: PlaitTableCell, direction: 'start' | 'end', isRow: boolean) {
const data = isRow ? element.rows : element.columns;
const id = isRow ? resizeCell.rowId : resizeCell.columnId;
const span = isRow ? resizeCell.rowspan : resizeCell.colspan;
let index = data.findIndex(item => item.id === id);
if (direction === 'end' && span && span !== 1) {
index += span - 1;
}
return index;
}

function getResizeColumnOrRowSize(board: PlaitBoard, element: PlaitBaseTable, index: number, isRow: boolean) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

getCurrentRowHeightOrColumnWidth vs getMinColumnWidthOrRowHeight

const size = isRow ? element.rows[index]['height'] : element.columns[index]['width'];
if (size) {
return size;
}
const id = isRow ? element.rows[index].id : element.columns[index].id;
const dimension = isRow ? 'height' : 'width';
const cellsWithPoints = getCellsWithPoints(board, element);
const minDimension = cellsWithPoints.reduce((acc, item) => {
const itemId = isRow ? item.rowId : item.columnId;
if (itemId === id) {
const rectangle = RectangleClient.getRectangleByPoints(item.points);
const itemSize = rectangle[dimension];
if (!acc || itemSize < acc) {
acc = itemSize;
}
}
return acc;
}, 0);

return minDimension;
}
20 changes: 16 additions & 4 deletions packages/draw/src/utils/table.ts
Original file line number Diff line number Diff line change
Expand Up @@ -107,15 +107,27 @@ export function getTextManageByCell(cell: PlaitTableCell) {
return getTextManage(cell.id);
}

export const updateColumns = (table: PlaitBaseTable, columnId: string, width: number, offset: number) => {
export const updateColumns = (
table: PlaitBaseTable,
columnId: string,
width: number,
offset: number,
direction: 'start' | 'end' = 'end'
) => {
const columns = table.columns.map(item => (item.id === columnId ? { ...item, width } : item));
const points = [table.points[0], [table.points[1][0] + offset, table.points[1][1]]] as Point[];
let points = [table.points[0], [table.points[1][0] + offset, table.points[1][1]]] as Point[];
if (direction === 'start') {
points = [[table.points[0][0] - offset, table.points[0][1]], table.points[1]] as Point[];
}
return { columns, points };
};

export const updateRows = (table: PlaitBaseTable, rowId: string, height: number, offset: number) => {
export const updateRows = (table: PlaitBaseTable, rowId: string, height: number, offset: number, direction: 'start' | 'end' = 'end') => {
const rows = table.rows.map(item => (item.id === rowId ? { ...item, height } : item));
const points = [table.points[0], [table.points[1][0], table.points[1][1] + offset]] as Point[];
let points = [table.points[0], [table.points[1][0], table.points[1][1] + offset]] as Point[];
if (direction === 'start') {
points = [[table.points[0][0], table.points[0][1] - offset], table.points[1]] as Point[];
}
return { rows, points };
};

Expand Down
Loading