figmatic-print-builder/elements/ImageElement.jsx

191 lines
6.3 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { Image as KonvaImage, Transformer, Rect, Text } from "react-konva";
import { useRef, useEffect, useState } from "react";
import useEditorStore from "../useEditorStore";
export default function ImageElement({ element }) {
const updateElement = useEditorStore((state) => state.updateElement);
const setSelectedId = useEditorStore((state) => state.setSelectedId);
const selectedId = useEditorStore((state) => state.selectedId);
const shapeRef = useRef();
const trRef = useRef();
const [image, setImage] = useState(null);
const [isHovered, setIsHovered] = useState(false);
const isSelected = selectedId === element.id;
useEffect(() => {
if (element.src) {
const img = new window.Image();
img.src = element.src;
img.onload = () => {
setImage(img);
};
} else {
setImage(null);
}
}, [element.src]);
useEffect(() => {
if (isSelected && trRef.current && shapeRef.current) {
trRef.current.nodes([shapeRef.current]);
trRef.current.getLayer().batchDraw();
}
}, [isSelected]);
const handleImageUpload = () => {
const input = document.createElement("input");
input.type = "file";
input.accept = "image/*";
input.onchange = (e) => {
const file = e.target.files[0];
if (file) {
const reader = new FileReader();
reader.onload = (event) => {
updateElement(element.id, { src: event.target.result });
};
reader.readAsDataURL(file);
}
};
input.click();
};
const handleDeleteImage = (e) => {
e.cancelBubble = true;
updateElement(element.id, { src: null });
};
if (!image) {
// Placeholder when no image
return (
<>
<Rect
ref={shapeRef}
x={element.x}
y={element.y}
width={element.width}
height={element.height}
fill="#f0f0f0"
stroke="#ddd"
strokeWidth={2}
dash={[5, 5]}
rotation={element.rotation}
draggable
onClick={() => {
setSelectedId(element.id);
handleImageUpload();
}}
onDragEnd={(e) => {
const snap = (v) => Math.round(v / 10) * 10;
updateElement(element.id, {
x: snap(e.target.x()),
y: snap(e.target.y()),
});
}}
/>
<Text
text="📷 Click to Upload"
x={element.x + element.width / 2 - 60}
y={element.y + element.height / 2 - 10}
fontSize={14}
fill="#999"
listening={false}
/>
{isSelected && (
<Transformer
ref={trRef}
rotateEnabled={true}
boundBoxFunc={(oldBox, newBox) => {
if (newBox.width < 20 || newBox.height < 20) {
return oldBox;
}
return newBox;
}}
/>
)}
</>
);
}
return (
<>
<KonvaImage
ref={shapeRef}
image={image}
x={element.x}
y={element.y}
width={element.width}
height={element.height}
rotation={element.rotation}
draggable
onClick={() => setSelectedId(element.id)}
onMouseEnter={() => setIsHovered(true)}
onMouseLeave={() => setIsHovered(false)}
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 = shapeRef.current;
const scaleX = node.scaleX();
const scaleY = node.scaleY();
node.scaleX(1);
node.scaleY(1);
updateElement(element.id, {
x: node.x(),
y: node.y(),
width: Math.max(5, node.width() * scaleX),
height: Math.max(5, node.height() * scaleY),
rotation: node.rotation(),
});
}}
/>
{/* Delete button on hover */}
{isHovered && (
<Rect
x={element.x + element.width - 30}
y={element.y + 5}
width={25}
height={25}
fill="#ff4444"
cornerRadius={4}
onClick={handleDeleteImage}
onTap={handleDeleteImage}
/>
)}
{isHovered && (
<Text
text="×"
x={element.x + element.width - 22}
y={element.y + 8}
fontSize={18}
fill="#fff"
fontStyle="bold"
onClick={handleDeleteImage}
onTap={handleDeleteImage}
/>
)}
{isSelected && (
<Transformer
ref={trRef}
rotateEnabled={true}
boundBoxFunc={(oldBox, newBox) => {
// Limit resize
if (newBox.width < 20 || newBox.height < 20) {
return oldBox;
}
return newBox;
}}
/>
)}
</>
);
}