Events
The Grid and GridItem components emit events when items change position or size.
Grid onchange
The Grid’s onchange callback fires whenever any item in the grid changes:
<script lang="ts">
import Grid, { GridItem, type LayoutChangeDetail } from '@appulsauce/svelte-grid';
function handleChange(detail: LayoutChangeDetail) {
console.log('Layout changed:', detail);
}
</script>
<Grid cols={10} onchange={handleChange}>
<!-- items -->
</Grid> GridItem onchange
Each GridItem can have its own onchange callback:
<script lang="ts">
import Grid, { GridItem, type LayoutChangeDetail } from '@appulsauce/svelte-grid';
function handleItemChange(detail: LayoutChangeDetail) {
console.log('Item changed:', detail);
}
</script>
<Grid cols={10}>
<GridItem x={0} y={0} onchange={handleItemChange}>
{#snippet children()}
<div>Tracked Item</div>
{/snippet}
</GridItem>
</Grid> GridItem onpreviewchange
The onpreviewchange callback fires during drag/resize, before the final position is committed:
<script lang="ts">
import Grid, { GridItem, type LayoutChangeDetail } from '@appulsauce/svelte-grid';
function handlePreviewChange(detail: LayoutChangeDetail) {
// Called while dragging, shows where item will land
console.log('Preview position:', detail);
}
</script>
<Grid cols={10}>
<GridItem x={0} y={0} onpreviewchange={handlePreviewChange}>
{#snippet children()}
<div>Drag me</div>
{/snippet}
</GridItem>
</Grid> LayoutChangeDetail Type
interface LayoutChangeDetail {
item: LayoutItem; // The item that changed
items: LayoutItem[]; // All items in the grid
}
interface LayoutItem {
id: string;
x: number;
y: number;
w: number;
h: number;
} Example: Persisting Layout
<script lang="ts">
import Grid, { GridItem, type LayoutChangeDetail } from '@appulsauce/svelte-grid';
import { onMount } from 'svelte';
let items = $state([
{ id: '1', x: 0, y: 0, w: 2, h: 2 },
{ id: '2', x: 2, y: 0, w: 3, h: 1 }
]);
onMount(() => {
const saved = localStorage.getItem('grid-layout');
if (saved) {
items = JSON.parse(saved);
}
});
function handleChange(detail: LayoutChangeDetail) {
// Save layout to localStorage
localStorage.setItem('grid-layout', JSON.stringify(detail.items));
}
const itemSize = { height: 50 };
</script>
<Grid {itemSize} cols={10} collision="push" onchange={handleChange}>
{#each items as item (item.id)}
<GridItem id={item.id} bind:x={item.x} bind:y={item.y} bind:w={item.w} bind:h={item.h}>
{#snippet children()}
<div class="item">{item.id}</div>
{/snippet}
</GridItem>
{/each}
</Grid> Example: Real-time Preview
<script lang="ts">
import Grid, { GridItem, type LayoutChangeDetail } from '@appulsauce/svelte-grid';
let previewInfo = $state<{ x: number; y: number; w: number; h: number } | null>(null);
function handlePreview(detail: LayoutChangeDetail) {
previewInfo = detail.item;
}
function handleChange() {
previewInfo = null;
}
const itemSize = { height: 50 };
</script>
{#if previewInfo}
<div class="fixed bottom-4 right-4 bg-black text-white p-2 rounded text-sm">
Position: ({previewInfo.x}, {previewInfo.y}) Size: {previewInfo.w}x{previewInfo.h}
</div>
{/if}
<Grid {itemSize} cols={10}>
<GridItem x={0} y={0} onpreviewchange={handlePreview} onchange={handleChange}>
{#snippet children()}
<div class="item">Drag to see position</div>
{/snippet}
</GridItem>
</Grid> Two-way Binding vs Events
You can use either two-way binding or events to track changes:
Two-way Binding (recommended for reactive state):
<GridItem bind:x={item.x} bind:y={item.y} bind:w={item.w} bind:h={item.h}> Events (useful for side effects):
<GridItem x={item.x} y={item.y} onchange={saveToDatabase}> You can also use both together:
<GridItem
bind:x={item.x}
bind:y={item.y}
bind:w={item.w}
bind:h={item.h}
onchange={persistLayout}
>