135 lines
4.8 KiB
TypeScript
135 lines
4.8 KiB
TypeScript
import React from "react";
|
|
import { Group, Rect, Text } from "react-konva";
|
|
import Konva from "konva";
|
|
import type { EditorObject } from "@/pages/editor/store/editorStore";
|
|
|
|
type TableProps = {
|
|
obj: EditorObject;
|
|
selectedCellId: string | null;
|
|
onCellClick: (tableId: string, cellId: string, node: Konva.Node, e?: Konva.KonvaEventObject<MouseEvent>) => void;
|
|
onCellDblClick: (tableId: string, cellId: string, x: number, y: number, width: number, height: number, text: string) => void;
|
|
onDragEnd: (tableId: string, x: number, y: number) => void;
|
|
draggable: boolean;
|
|
};
|
|
|
|
const Table = ({
|
|
obj,
|
|
selectedCellId,
|
|
onCellClick,
|
|
onCellDblClick,
|
|
onDragEnd,
|
|
draggable,
|
|
}: TableProps) => {
|
|
const groupRef = React.useRef<Konva.Group>(null);
|
|
const tableData = obj.tableData;
|
|
|
|
React.useEffect(() => {
|
|
if (groupRef.current) {
|
|
groupRef.current.setAttr("id", obj.id);
|
|
}
|
|
}, [obj.id]);
|
|
|
|
if (!tableData) return null;
|
|
|
|
const { rows, cols, cells, cellWidth, cellHeight, stroke, strokeWidth } = tableData;
|
|
|
|
const handleCellClick = (cellId: string, e: Konva.KonvaEventObject<MouseEvent>) => {
|
|
e.cancelBubble = true;
|
|
if (groupRef.current) {
|
|
onCellClick(obj.id, cellId, groupRef.current, e);
|
|
}
|
|
};
|
|
|
|
const handleCellDblClick = (cellId: string, e: Konva.KonvaEventObject<MouseEvent>) => {
|
|
e.cancelBubble = true;
|
|
const stage = e.target.getStage();
|
|
if (!stage || !groupRef.current) return;
|
|
|
|
const cell = cells[cellId];
|
|
if (!cell) return;
|
|
|
|
// Get the target node (Rect or Text)
|
|
const targetNode = e.target;
|
|
|
|
// Get the client rect which includes all transformations (scale, position, etc.)
|
|
const box = targetNode.getClientRect();
|
|
const stageBox = stage.container().getBoundingClientRect();
|
|
|
|
// Calculate position relative to viewport
|
|
const x = stageBox.left + box.x;
|
|
const y = stageBox.top + box.y;
|
|
const width = box.width;
|
|
const height = box.height;
|
|
|
|
onCellDblClick(obj.id, cellId, x, y, width, height, cell.text);
|
|
};
|
|
|
|
const handleDragEnd = () => {
|
|
if (groupRef.current) {
|
|
const pos = groupRef.current.position();
|
|
onDragEnd(obj.id, pos.x, pos.y);
|
|
}
|
|
};
|
|
|
|
return (
|
|
<Group
|
|
ref={groupRef}
|
|
x={obj.x}
|
|
y={obj.y}
|
|
draggable={draggable}
|
|
onDragEnd={handleDragEnd}
|
|
>
|
|
{Array.from({ length: rows }).map((_, row) =>
|
|
Array.from({ length: cols }).map((_, col) => {
|
|
const cellId = `cell-${row}-${col}`;
|
|
const cell = cells[cellId];
|
|
if (!cell) return null;
|
|
|
|
const x = col * cellWidth;
|
|
const y = row * cellHeight;
|
|
const isCellSelected = selectedCellId === cellId;
|
|
|
|
const isTopLeft = row === 0 && col === 0;
|
|
const isTopRight = row === 0 && col === cols - 1;
|
|
|
|
return (
|
|
<Group key={cellId}>
|
|
<Rect
|
|
x={x}
|
|
y={y}
|
|
width={cellWidth}
|
|
height={cellHeight}
|
|
fill={cell.background}
|
|
stroke={isCellSelected ? "#3b82f6" : stroke || "#808080"}
|
|
strokeWidth={isCellSelected ? 3 : strokeWidth || 1}
|
|
cornerRadius={isTopLeft ? [8, 0, 0, 0] : isTopRight ? [0, 8, 0, 0] : 0}
|
|
onClick={(e) => handleCellClick(cellId, e)}
|
|
onDblClick={(e) => handleCellDblClick(cellId, e)}
|
|
/>
|
|
{cell.text && (
|
|
<Text
|
|
x={x}
|
|
y={y}
|
|
width={cellWidth}
|
|
height={cellHeight}
|
|
text={cell.text}
|
|
fontSize={14}
|
|
fontFamily="Arial"
|
|
fill="#000000"
|
|
align="center"
|
|
verticalAlign="middle"
|
|
onClick={(e) => handleCellClick(cellId, e)}
|
|
onDblClick={(e) => handleCellDblClick(cellId, e)}
|
|
/>
|
|
)}
|
|
</Group>
|
|
);
|
|
})
|
|
)}
|
|
</Group>
|
|
);
|
|
};
|
|
|
|
export default Table;
|
|
|