Skip to content

Sidcom-AB/trakk

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

22 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

trakk

Lightweight timeline editor web component
Zero dependencies. Works everywhere.

Installation • Usage • API • Events • Styling • Demo

trakk demo


What is it?

A timeline editor for building animation tools, video editors, audio sequencers, or anything that needs time-based sequencing. Built as a native Web Component with zero dependencies.

Installation

npm install @andymcloid/trakk

Or use directly via CDN:

<script type="module" src="https://unpkg.com/@andymcloid/trakk/dist/trakk.esm.js"></script>
<link rel="stylesheet" href="https://unpkg.com/@andymcloid/trakk/dist/trakk.css">

Usage

<trakk-editor id="timeline"></trakk-editor>

<script type="module">
  import { Trakk } from '@andymcloid/trakk';
  import '@andymcloid/trakk/css';

  const timeline = document.getElementById('timeline');

  timeline.setData([
    {
      id: 'track-1',
      name: 'Audio',
      blocks: [
        { id: 'block-1', name: 'Intro', start: 0, end: 5 },
        { id: 'block-2', name: 'Main', start: 6, end: 15 }
      ]
    },
    {
      id: 'track-2',
      name: 'Video',
      blocks: [
        { id: 'block-3', name: 'Scene 1', start: 0, end: 10 }
      ]
    }
  ]);

  // Play/pause
  timeline.play({ autoEnd: true });
  timeline.pause();

  // Listen for changes
  timeline.addEventListener('change', (e) => {
    console.log('Updated:', e.detail.tracks);
  });
</script>

API

Methods

Method Description
setData(tracks) Set timeline data
play(options?) Start playback. Options: { autoEnd: boolean, toTime: number }
pause() Pause playback
setTime(time) Set current time position
getTime() Get current time
getTotalTime() Get total duration (end of last block)
setConfig(config) Update configuration
selectAction(action, row?) Select a block programmatically
deselectAction() Clear the current selection
getSelectedAction() Get currently selected block { action, row } or null
saveToLocalStorage(key?) Save to localStorage
loadFromLocalStorage(key?) Load from localStorage

Configuration

timeline.setConfig({
  scale: 1,              // Seconds per scale unit
  scaleWidth: 160,       // Pixels per scale unit
  scaleCount: 20,        // Number of scale units
  startLeft: 100,        // Left margin (label column width)
  rowHeight: 32,         // Track height in pixels
  autoScroll: true,      // Auto-scroll during playback
  hideCursor: false,     // Hide playhead cursor
  disableDrag: false,    // Disable all dragging
  allowOverlap: false    // Allow blocks to overlap (default: false)
});

Data Structure

interface Track {
  id: string;
  name?: string;
  locked?: boolean;      // Prevent editing
  blocks: Block[];
}

interface Block {
  id: string;
  name?: string;
  start: number;         // Start time in seconds
  end: number;           // End time in seconds
  locked?: boolean;      // Prevent editing this block
}

Events

// Data changed (drag, resize, create, delete)
timeline.addEventListener('change', (e) => {
  console.log(e.detail.tracks);
});

// New block created via drag
timeline.addEventListener('itemcreated', (e) => {
  console.log(e.detail.item, e.detail.row);
});

// Block deleted
timeline.addEventListener('blockdeleted', (e) => {
  console.log(e.detail.block, e.detail.track);
});

// Track deleted
timeline.addEventListener('trackdeleted', (e) => {
  console.log(e.detail.track);
});

// Block selected/deselected
timeline.addEventListener('select', (e) => {
  if (e.detail) {
    console.log('Selected:', e.detail.action, 'in track:', e.detail.row);
  } else {
    console.log('Selection cleared');
  }
});

// Track/block renamed (double-click to edit)
timeline.addEventListener('trackrenamed', (e) => {
  console.log(e.detail.track, e.detail.name);
});

timeline.addEventListener('blockrenamed', (e) => {
  console.log(e.detail.block, e.detail.name);
});

Engine Events

Access the playback engine directly:

timeline.engine.on('play', () => console.log('Playing'));
timeline.engine.on('paused', () => console.log('Paused'));
timeline.engine.on('ended', () => console.log('Ended'));

timeline.engine.on('setTimeByTick', ({ time }) => {
  // Called every frame during playback
  console.log('Current time:', time);
});

Callbacks

For advanced control over interactions:

timeline.setCallbacks({
  // Custom rendering
  getActionRender: (block, track) => `<b>${block.name}</b>`,
  getScaleRender: (time) => `${time.toFixed(1)}s`,

  // Interaction hooks (return false to cancel)
  onActionMoving: ({ action, start, end }) => {
    if (start < 0) return false; // Prevent moving before 0
  },
  onActionResizing: ({ action, start, end }) => {
    if (end - start < 0.5) return false; // Minimum 0.5s duration
  },

  // Click handlers (button: 0=left, 1=middle, 2=right)
  onClickAction: (e, { action, row, time, button }) => {},
  onDoubleClickAction: (e, { action, row, time }) => {},
  onContextMenuAction: (e, { action, row, time, button }) => {
    // Right-click on block - show custom context menu
  },
  onClickRow: (e, { row, time }) => {},
  onClickTimeArea: (e, { time }) => {}
});

Styling

Customize Trakk's appearance using CSS custom properties:

trakk-editor {
  /* Core colors */
  --trakk-bg: #191b1d;           /* Background color */
  --trakk-text: #ffffff;         /* Text color */
  --trakk-accent: #5297FF;       /* Accent color (cursor, selection border) */

  /* Block colors */
  --trakk-block-bg: #2f3134;           /* Block background */
  --trakk-block-bg-hover: #3a3d40;     /* Block hover state */
  --trakk-block-bg-selected: #4a7ba7;  /* Selected block background */

  /* Borders */
  --trakk-border: rgba(255, 255, 255, 0.1);        /* Subtle borders */
  --trakk-border-strong: rgba(255, 255, 255, 0.3); /* Prominent borders */

  /* Text variations */
  --trakk-text-muted: rgba(255, 255, 255, 0.6);  /* Secondary text */
  --trakk-text-subtle: rgba(255, 255, 255, 0.4); /* Subtle text/icons */

  /* State */
  --trakk-danger: #ff6464;       /* Delete button hover */
  --trakk-disabled-opacity: 0.6; /* Disabled elements */
  --trakk-locked-opacity: 0.7;   /* Locked elements */
}

Light Theme Example

trakk-editor.light {
  --trakk-bg: #f5f5f5;
  --trakk-text: #1a1a1a;
  --trakk-accent: #0066cc;
  --trakk-block-bg: #e0e0e0;
  --trakk-block-bg-hover: #d0d0d0;
  --trakk-block-bg-selected: #b3d4fc;
  --trakk-border: rgba(0, 0, 0, 0.1);
  --trakk-border-strong: rgba(0, 0, 0, 0.2);
  --trakk-text-muted: rgba(0, 0, 0, 0.6);
  --trakk-text-subtle: rgba(0, 0, 0, 0.4);
}

Demo

Open demo.html in a browser or check out the live demo.

License

MIT