Closed harrysayers closed 1 week ago
How do you want to represent it in state? Additional state with groups? Or inside the same arrayofelements
with some nested structure?
That is one of the things that i'm struggling with... Currently I have one array of objects that stores all components. I want one component (let's call it 'container') that when another object is dragged over the top it's grouped with the 'container'... I was thinking that there would be a property in the array called "grouped: bool" & "grouped_by: Id of the container" and you could use those to change how the components are rendered ? Again, struggling with this so any help is much appreciated.
@lavrton have you got any thoughts on this... have been trying to work this out for a few days to no avail.
Any help is much appreciated!
I would suggest having different types of elements in the state. So you may have a "group" or "container" that may have children in it. On drag end, you can check overlaps and move an element into such container.
import { createRoot } from "react-dom/client";
import React, { useState } from "react";
import { Stage, Layer, Rect, Group } from "react-konva";
const App = () => {
const [elements, setElements] = useState([
{
id: 1,
type: "container",
x: 50,
y: 50,
width: 100,
height: 100,
children: [],
},
{ id: 2, type: "element", x: 200, y: 200, width: 50, height: 50 },
{ id: 3, type: "element", x: 300, y: 300, width: 50, height: 50 },
// Add more elements as needed
]);
const handleDragEnd = (id, newPos) => {
setElements((prevElements) => {
const draggedElement = prevElements.find((el) => el.id === id);
const container = prevElements.find(
(el) =>
el.id !== id &&
el.type === "container" &&
isInsideContainer(newPos, el) // Check if the dragged item is inside this container
);
if (container) {
// Move dragged element to the container’s children
return prevElements
.map((el) => {
if (el.id === container.id) {
return {
...el,
children: [
...el.children,
{ ...draggedElement, x: newPos.x - el.x, y: newPos.y - el.y },
],
};
}
return el.id === id ? null : el;
})
.filter(Boolean); // Filter out the dragged element from the main array
}
// If no container, update position as usual
return prevElements.map((el) =>
el.id === id ? { ...el, x: newPos.x, y: newPos.y } : el
);
});
};
const isInsideContainer = (pos, container) => {
return (
pos.x > container.x &&
pos.x < container.x + container.width &&
pos.y > container.y &&
pos.y < container.y + container.height
);
};
return (
<Stage width={window.innerWidth} height={window.innerHeight}>
<Layer>
{elements.map((el) =>
el.type === "container" ? (
<Group
key={el.id}
x={el.x}
y={el.y}
draggable
onDragEnd={(e) => handleDragEnd(el.id, e.target.position())}
>
<Rect width={el.width} height={el.height} fill="lightblue" />
{el.children.map((child) => (
<Rect
key={child.id}
x={child.x}
y={child.y}
width={child.width}
height={child.height}
fill="blue"
/>
))}
</Group>
) : (
<Rect
key={el.id}
x={el.x}
y={el.y}
width={el.width}
height={el.height}
fill="red"
draggable
onDragEnd={(e) => handleDragEnd(el.id, e.target.position())}
/>
)
)}
</Layer>
</Stage>
);
};
export default App;
const container = document.getElementById("root");
const root = createRoot(container);
root.render(<App />);
https://codesandbox.io/p/sandbox/react-konva-drop-into-container-2q2d8k
Thanks you for this. I've nearly implemented this, however, there is a really strange bug/side effect happening with this approach...
Inside my container i'm iterating over the container children (like you have above) which looks like this.
<Group
key={el.id}
x={el.x}
y={el.y}
draggable
onDragEnd={(e) => handleDrag()}
>
<Rect width={el.width} height={el.height} fill="lightblue" />
{el.children.map((child) => (
if(child.type == 'a'){
<compA item={child}/>
}else if(child.type == 'b'){
<compB item={child}/>
}
))}
</Group>
the issue is that when I'm moving the child components (who have OnDrag functions as well) it's triggering the onDragEnd function of the container without me knowing as it's not updating on the canvas...
do you know why this is happening?
This may help: https://konvajs.org/docs/events/Cancel_Propagation.html
I'm calling e.evt.cancelBubble = true;
in my child mouse event however when I console.log the event cancelBubble
is still set as false and not preventing the parent triggering.
Actually, I did a restart of everything and now it is working... Thank you very much for all your help @lavrton.
I will close this now and hopefully it will help someone else in the future.
Hello, I'm posting an issue as I've looked on StackOverflow, Git and the Docs and can't seem to find anyway of doing the following.
I have a stage with lots components (Shapes & HTML elements) when I drag one of these elements on to another I want to group those components together. I can't think of a way of doing this so any help would be appreciated.
This is the how my app looks