svelte-dnd-timeline Svelte Themes

Svelte Dnd Timeline

svelte-dnd-timeline

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.

Features

  • Drag items horizontally to move them in time
  • Drag items vertically to move them between rows
  • Resize items by dragging the left or right edges
  • Grid snapping for precise alignment
  • Real-time visual feedback during drag operations
  • Type-safe with full TypeScript support
  • Headless & Composable - You control the layout and styling
  • Built with Svelte 5 and modern runes API

Installation

npm install svelte-dnd-timeline @atlaskit/pragmatic-drag-and-drop @atlaskit/pragmatic-drag-and-drop-auto-scroll

Usage

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>

API

Components

<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 }.

Types

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;
}

Developing

pnpm install
pnpm run dev

License

MIT

Top categories

Loading Svelte Themes