I have a parent component with multiple draggable Item-components which will be created dynamically in the final app. Inside each Item I calculate the current position as well as a derived offset value. Each time, any Item is dragged I want its position/offset to be shared reactively with the parent component.
In Svelte 4 with stores this was fairly easy. However, I struggle implementing this in Svelte 5 with runes. Context-API somehow isn't an option, since I'd like to define pos/offset inside the Item-component where they belong... shouldn't I?
Here is my current code: REPL
The current code has two issues:
- (1) While sending the position (state) to the parent via bound props, this does not work for the offset (derived). Why?
- (2) As soon as the second item-component is mounted, the connection between the first item and the parent is lost. Why?
Update
Reduced code (without timers): REPL
App.svelte
<svelte:options runes="{true}" /><script> import Item from './Item.svelte'; let CURRENT = $state({pos: {x:99, y:99}, offset: 99});</script><h1>Last dragged item: ({CURRENT.pos.x} / {CURRENT.pos.y}) Offset: {CURRENT.offset}</h1><Item bind:CURRENT={CURRENT} itemName='ONE' /><Item bind:CURRENT={CURRENT} itemName='TWO' />
Item.svelte
<svelte:options runes="{true}" /><script> import interact from 'interactjs'; let { CURRENT = $bindable(), itemName } = $props(); let item; let pos = $state({x:0, y:0}); let offset = $derived(Math.floor(Math.sqrt( Math.pow(pos.x,2) + Math.pow(pos.y,2) ))); CURRENT.pos = pos; CURRENT.offset = offset; const handleDraggable = (node) => { interact(node).draggable({ onmove: (ev) => { let el = ev.target; let x = (parseFloat(el.getAttribute('data-x')) || 0) + ev.dx; let y = (parseFloat(el.getAttribute('data-y')) || 0) + ev.dy; el.style.webkitTransform = el.style.transform = `translate(${x}px,${y}px)`; el.setAttribute('data-x', x); el.setAttribute('data-y', y); pos.x = x; pos.y = y; }, }); }</script><div id="draggable" bind:this={item} use:handleDraggable> <p>{itemName} ({pos.x} / {pos.y}) Offset: {offset}</p></div>