A powerful drag-and-drop timeline component for Svelte 5, built with @atlaskit/pragmatic-drag-and-drop.
This is a conceptual port of dnd-timeline to Svelte, featuring precise pixel manipulation and layout control for timeline-based interfaces.
npm install svelte-dnd-timeline @atlaskit/pragmatic-drag-and-drop @atlaskit/pragmatic-drag-and-drop-auto-scroll
This library provides a headless, composable set of components. You are responsible for iterating over your rows and items, which gives you full control over the markup and styling.
<script lang="ts">
import { Timeline, TimelineRow, TimelineItem } from 'svelte-dnd-timeline';
import type { Item, Row, TimelineOptions } from 'svelte-dnd-timeline';
const rows: Row[] = [
{ id: 'row-1', label: 'Team A' },
{ id: 'row-2', label: 'Team B' },
];
// Items must be reactive (using $state) if you want the UI to update
const items = $state<Item[]>([
{ id: '1', rowId: 'row-1', span: { start: 100, end: 300 }, title: 'Task 1' },
{ id: '2', rowId: 'row-2', span: { start: 400, end: 600 }, title: 'Task 2' },
]);
const options: TimelineOptions = {
items,
rows,
range: { start: 0, end: 1000 },
gridSize: 50
};
</script>
<!-- You can pass any class to style the container -->
<Timeline {options} class="timeline-container">
<!-- You can build your own header here -->
<div class="header">...</div>
<!-- Render Rows -->
{#each rows as row (row.id)}
<div class="row-wrapper">
<div class="sidebar">{row.label}</div>
<!-- The Droppable Row Area -->
<TimelineRow id={row.id} class="row-track">
{#each items.filter(i => i.rowId === row.id) as item (item.id)}
<!-- The Draggable Item -->
<TimelineItem {item} class="item-style">
<!-- Custom Content Snippet -->
{#snippet children({ item })}
<span>{item.title}</span>
{/snippet}
</TimelineItem>
{/each}
</TimelineRow>
</div>
{/each}
</Timeline>
<style>
.timeline-container {
border: 1px solid #ccc;
width: 100%;
}
.row-wrapper {
display: flex;
height: 50px;
border-bottom: 1px solid #eee;
}
.sidebar {
width: 100px;
border-right: 1px solid #eee;
}
/* TimelineRow needs relative positioning usually handled by the lib,
but you can add background colors etc */
:global(.row-track) {
flex: 1;
background: #f9f9f9;
}
/* TimelineItem handles positioning, you handle the look */
:global(.item-style) {
background: #e0f2fe;
border: 1px solid #7dd3fc;
border-radius: 4px;
}
</style>
<Timeline>The root container.
options: TimelineOptions - Configuration object.class: string - CSS class for the wrapper.<TimelineRow>A droppable zone for items.
id: string - The row ID (must match row.id).class: string - CSS class.<TimelineItem>A draggable and resizable item.
item: Item - The item data object.class: string - CSS class.children: Snippet - Render prop for item content. Receives { item, isDragging }.interface TimelineOptions {
items: Item[];
rows: Row[];
range?: { start: number; end: number };
gridSize?: number;
magnetism?: number;
minWidth?: number;
collision?: boolean;
}
interface Item {
id: string;
rowId: string;
span: { start: number; end: number };
title?: string;
[key: string]: any;
}
pnpm install
pnpm run dev
MIT