131 lines
5.0 KiB
React
131 lines
5.0 KiB
React
|
|
import { Group, Rect, Text, Transformer } from "react-konva";
|
||
|
|
import { useRef, useEffect } from "react";
|
||
|
|
import useEditorStore from "../useEditorStore";
|
||
|
|
|
||
|
|
export default function TableElement({ element }) {
|
||
|
|
const updateElement = useEditorStore((state) => state.updateElement);
|
||
|
|
const setSelectedId = useEditorStore((state) => state.setSelectedId);
|
||
|
|
const selectedId = useEditorStore((state) => state.selectedId);
|
||
|
|
|
||
|
|
const groupRef = useRef();
|
||
|
|
const trRef = useRef();
|
||
|
|
|
||
|
|
const isSelected = selectedId === element.id;
|
||
|
|
|
||
|
|
useEffect(() => {
|
||
|
|
if (isSelected && trRef.current && groupRef.current) {
|
||
|
|
trRef.current.nodes([groupRef.current]);
|
||
|
|
trRef.current.getLayer().batchDraw();
|
||
|
|
}
|
||
|
|
}, [isSelected]);
|
||
|
|
|
||
|
|
const visibleColumns = element.columns.filter((col) => col.visible);
|
||
|
|
const totalWidth = visibleColumns.reduce((sum, col) => sum + col.width, 0);
|
||
|
|
|
||
|
|
return (
|
||
|
|
<>
|
||
|
|
<Group
|
||
|
|
ref={groupRef}
|
||
|
|
x={element.x}
|
||
|
|
y={element.y}
|
||
|
|
rotation={element.rotation}
|
||
|
|
draggable
|
||
|
|
onClick={() => setSelectedId(element.id)}
|
||
|
|
onDragEnd={(e) => {
|
||
|
|
const snap = (v) => Math.round(v / 10) * 10;
|
||
|
|
updateElement(element.id, {
|
||
|
|
x: snap(e.target.x()),
|
||
|
|
y: snap(e.target.y()),
|
||
|
|
});
|
||
|
|
}}
|
||
|
|
onTransformEnd={() => {
|
||
|
|
const node = groupRef.current;
|
||
|
|
updateElement(element.id, {
|
||
|
|
rotation: node.rotation(),
|
||
|
|
});
|
||
|
|
}}
|
||
|
|
>
|
||
|
|
{/* Header Row */}
|
||
|
|
{visibleColumns.map((col, i) => {
|
||
|
|
const xPos = visibleColumns
|
||
|
|
.slice(0, i)
|
||
|
|
.reduce((sum, c) => sum + c.width, 0);
|
||
|
|
|
||
|
|
return (
|
||
|
|
<Group key={col.key}>
|
||
|
|
<Rect
|
||
|
|
x={xPos}
|
||
|
|
y={0}
|
||
|
|
width={col.width}
|
||
|
|
height={element.rowHeight}
|
||
|
|
fill={element.headerBg}
|
||
|
|
stroke={element.borderColor}
|
||
|
|
strokeWidth={1}
|
||
|
|
/>
|
||
|
|
<Text
|
||
|
|
text={col.label}
|
||
|
|
x={xPos + 5}
|
||
|
|
y={element.rowHeight / 2 - 7}
|
||
|
|
fontSize={12}
|
||
|
|
fontStyle="bold"
|
||
|
|
fill="#000"
|
||
|
|
/>
|
||
|
|
</Group>
|
||
|
|
);
|
||
|
|
})}
|
||
|
|
|
||
|
|
{/* Sample Data Rows */}
|
||
|
|
{element.showSampleData && element.sampleData && element.sampleData.map((row, rowIdx) => (
|
||
|
|
<Group key={rowIdx}>
|
||
|
|
{visibleColumns.map((col, colIdx) => {
|
||
|
|
const xPos = visibleColumns
|
||
|
|
.slice(0, colIdx)
|
||
|
|
.reduce((sum, c) => sum + c.width, 0);
|
||
|
|
const yPos = element.rowHeight * (rowIdx + 1);
|
||
|
|
|
||
|
|
return (
|
||
|
|
<Group key={`${rowIdx}-${col.key}`}>
|
||
|
|
<Rect
|
||
|
|
x={xPos}
|
||
|
|
y={yPos}
|
||
|
|
width={col.width}
|
||
|
|
height={element.rowHeight}
|
||
|
|
stroke={element.borderColor}
|
||
|
|
strokeWidth={1}
|
||
|
|
fill="#fff"
|
||
|
|
/>
|
||
|
|
<Text
|
||
|
|
text={row[col.key] || ""}
|
||
|
|
x={xPos + 5}
|
||
|
|
y={yPos + element.rowHeight / 2 - 7}
|
||
|
|
fontSize={11}
|
||
|
|
fill="#000"
|
||
|
|
/>
|
||
|
|
</Group>
|
||
|
|
);
|
||
|
|
})}
|
||
|
|
</Group>
|
||
|
|
))}
|
||
|
|
</Group>
|
||
|
|
|
||
|
|
{isSelected && (
|
||
|
|
<Transformer
|
||
|
|
ref={trRef}
|
||
|
|
enabledAnchors={[
|
||
|
|
"top-left",
|
||
|
|
"top-right",
|
||
|
|
"bottom-left",
|
||
|
|
"bottom-right",
|
||
|
|
]}
|
||
|
|
rotateEnabled={true}
|
||
|
|
boundBoxFunc={(oldBox, newBox) => {
|
||
|
|
if (newBox.width < 100 || newBox.height < 50) {
|
||
|
|
return oldBox;
|
||
|
|
}
|
||
|
|
return newBox;
|
||
|
|
}}
|
||
|
|
/>
|
||
|
|
)}
|
||
|
|
</>
|
||
|
|
);
|
||
|
|
}
|