{"id":28422913,"url":"https://github.com/gravity-ui/timeline","last_synced_at":"2026-02-12T10:06:54.969Z","repository":{"id":296205880,"uuid":"975493081","full_name":"gravity-ui/timeline","owner":"gravity-ui","description":"timeline library","archived":false,"fork":false,"pushed_at":"2025-07-30T14:42:46.000Z","size":835,"stargazers_count":2,"open_issues_count":0,"forks_count":1,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-07-30T15:37:41.666Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/gravity-ui.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":"CODEOWNERS","security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2025-04-30T12:04:22.000Z","updated_at":"2025-07-30T14:40:49.000Z","dependencies_parsed_at":"2025-07-15T15:22:51.629Z","dependency_job_id":"78db44c8-73e2-4590-ae25-3b76d6ed5e7f","html_url":"https://github.com/gravity-ui/timeline","commit_stats":null,"previous_names":["gravity-ui/timeline"],"tags_count":17,"template":false,"template_full_name":null,"purl":"pkg:github/gravity-ui/timeline","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gravity-ui%2Ftimeline","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gravity-ui%2Ftimeline/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gravity-ui%2Ftimeline/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gravity-ui%2Ftimeline/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/gravity-ui","download_url":"https://codeload.github.com/gravity-ui/timeline/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gravity-ui%2Ftimeline/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":267998245,"owners_count":24178491,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-07-31T02:00:08.723Z","response_time":66,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2025-06-05T07:30:24.762Z","updated_at":"2026-02-12T10:06:54.963Z","avatar_url":"https://github.com/gravity-ui.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# @gravity-ui/timeline [![npm package](https://img.shields.io/npm/v/@gravity-ui/timeline)](https://www.npmjs.com/package/@gravity-ui/timeline) [![Release](https://img.shields.io/github/actions/workflow/status/gravity-ui/timeline/release.yml?branch=main\u0026label=Release)](https://github.com/gravity-ui/timeline/actions/workflows/release.yml?query=branch:main) [![storybook](https://img.shields.io/badge/Storybook-deployed-ff4685)](https://preview.gravity-ui.com/timeline/)\n\n\u003e [Русская версия](./README-ru.md)\n\nA React-based library for building interactive timeline visualizations with canvas rendering.\n\n## Documentation\n\nFor details see [Documentation](./docs/docs.md).\n\n## Preview\n\nBasic timeline with events and axes:\n\n![Basic timeline with events](./docs/img/lines.png)\n\nCustom rendering with expandable nested events ([NestedEvents](https://preview.gravity-ui.com/timeline/?path=/story/integrations-gravity-ui--nested-events-story) example):\n\n![Nested events timeline](./docs/img/events.png)\n\n## Features\n\n- Canvas-based rendering for high performance\n- Interactive timeline with zoom and pan capabilities\n- Support for events, markers, sections, axes, and grid\n- Background sections for visual organization and time period highlighting\n- Smart marker grouping with automatic zoom to group - Click on grouped markers to zoom into their individual components\n- Virtualized rendering for improved performance with large datasets (only active when timeline content exceeds the viewport)\n- Customizable appearance and behavior\n- TypeScript support with full type definitions\n- React integration with custom hooks\n\n## Installation\n\n```bash\nnpm install @gravity-ui/timeline\n```\n\n## Usage\n\nThe timeline component can be used in React applications with the following basic setup:\n\n```tsx\nimport { TimelineCanvas, useTimeline } from '@gravity-ui/timeline/react';\n\nconst MyTimelineComponent = () =\u003e {\n  const { timeline, api, start, stop } = useTimeline({\n    settings: {\n      start: Date.now(),\n      end: Date.now() + 3600000, // 1 hour from now\n      axes: [],\n      events: [],\n      markers: [],\n      sections: []\n    },\n    viewConfiguration: {\n      // Optional view configuration\n    }\n  });\n\n  // timeline - Timeline instance\n  // api - CanvasApi instance (same as timeline.api)\n  // start - function to initialize timeline with canvas\n  // stop - function to destroy timeline\n\n  return (\n    \u003cdiv style={{ width: '100%', height: '100%' }}\u003e\n      \u003cTimelineCanvas timeline={timeline} /\u003e\n    \u003c/div\u003e\n  );\n};\n```\n\n### Axis Structure\n\nEach axis has the following structure:\n\n```typescript\ntype TimelineAxis = {\n  id: string;          // Unique axis identifier\n  tracksCount: number; // Number of tracks in the axis\n  top: number;         // Vertical position (px)\n  height: number;      // Height per track (px)\n};\n```\n\n### Section Structure\n\nEach section requires the following structure:\n\n```typescript\ntype TimelineSection = {\n  id: string;               // Unique section identifier\n  from: number;             // Start timestamp\n  to?: number;              // Optional end timestamp (defaults to timeline end)\n  color: string;            // Background color of the section\n  hoverColor?: string;      // Optional color when section is hovered\n  renderer?: AbstractSectionRenderer; // Optional custom renderer (exported from the package)\n};\n```\n\nSections provide background coloring for time periods and help organize timeline content visually:\n\n```tsx\nconst MyTimelineComponent = () =\u003e {\n  const { timeline } = useTimeline({\n    settings: {\n      start: Date.now(),\n      end: Date.now() + 3600000,\n      axes: [],\n      events: [],\n      markers: [],\n      sections: [\n        {\n          id: 'morning',\n          from: Date.now(),\n          to: Date.now() + 1800000, // 30 minutes\n          color: 'rgba(255, 235, 59, 0.3)', // Semi-transparent yellow\n          hoverColor: 'rgba(255, 235, 59, 0.4)'\n        },\n        {\n          id: 'afternoon',\n          from: Date.now() + 1800000,\n          // No 'to' specified - extends to timeline end\n          color: 'rgba(76, 175, 80, 0.2)', // Semi-transparent green\n          hoverColor: 'rgba(76, 175, 80, 0.3)'\n        }\n      ]\n    },\n    viewConfiguration: {\n      sections: {\n        hitboxPadding: 2 // Hover detection padding\n      }\n    }\n  });\n\n  return \u003cTimelineCanvas timeline={timeline} /\u003e;\n};\n```\n\n### Marker Structure\n\nEach marker requires the following structure:\n\n```typescript\ntype TimelineMarker = {\n  time: number;           // Timestamp for the marker position\n  color: string;          // Color of the marker line\n  activeColor: string;    // Color when marker is selected (required)\n  hoverColor: string;     // Color when marker is hovered (required)\n  lineWidth?: number;     // Optional width of the marker line\n  label?: string;         // Optional label text\n  labelColor?: string;    // Optional label color\n  renderer?: AbstractMarkerRenderer; // Optional custom renderer\n  nonSelectable?: boolean;// Whether marker can be selected\n  group?: boolean;        // Whether marker represents a group\n};\n```\n\n### Marker Grouping and Zoom\n\nThe timeline automatically groups markers that are close together and provides zoom functionality:\n\n```tsx\nconst MyTimelineComponent = () =\u003e {\n  const { timeline } = useTimeline({\n    settings: {\n      start: Date.now(),\n      end: Date.now() + 3600000,\n      axes: [],\n      events: [],\n      markers: [\n        // These markers will be grouped together\n        { time: Date.now(), color: '#ff0000', activeColor: '#ff5252', hoverColor: '#ff1744', label: 'Event 1' },\n        { time: Date.now() + 1000, color: '#ff0000', activeColor: '#ff5252', hoverColor: '#ff1744', label: 'Event 2' },\n        { time: Date.now() + 2000, color: '#ff0000', activeColor: '#ff5252', hoverColor: '#ff1744', label: 'Event 3' },\n      ]\n    },\n    viewConfiguration: {\n      markers: {\n        collapseMinDistance: 8,        // Group markers within 8 pixels\n        groupZoomEnabled: true,        // Enable zoom on group click\n        groupZoomPadding: 0.3,        // 30% padding around group\n        groupZoomMaxFactor: 0.3,      // Max zoom factor\n      }\n    }\n  });\n\n  // Listen for group zoom events\n  useTimelineEvent(timeline, 'on-group-marker-click', (data) =\u003e {\n    console.log('Group zoomed:', data);\n  });\n\n  return \u003cTimelineCanvas timeline={timeline} /\u003e;\n};\n```\n\n## How It Works\n\nThe timeline component is built using React and provides a flexible way to create interactive timeline visualizations. Here's how it works:\n\n### Component Architecture\n\nThe timeline is implemented as a React component that can be configured through two main objects:\n\n1. **TimelineSettings**: Controls the core timeline behavior and appearance\n   - `start`: Start time of the timeline\n   - `end`: End time of the timeline\n   - `axes`: Array of axis configurations (see structure below)\n   - `events`: Array of event configurations\n   - `markers`: Array of marker configurations\n   - `sections`: Array of section configurations\n\n2. **ViewConfiguration**: Manages the visual representation and interaction settings\n   - Controls appearance, zoom levels, and interaction behavior\n   - Can be customized or use default values\n\n### Event Handling\n\nThe timeline component supports several interactive events:\n\n- `on-click`: Triggered when clicking on the timeline\n- `on-context-click`: Triggered on right-click/context menu\n- `on-select-change`: Fired when the selection changes\n- `on-hover`: Triggered when hovering over timeline elements\n- `on-leave`: Fired when the mouse leaves timeline elements\n\nExample of event handling:\n\n```tsx\nimport { useTimelineEvent } from '@gravity-ui/timeline/react';\n\nconst MyTimelineComponent = () =\u003e {\n  const { timeline } = useTimeline({ /* ... */ });\n\n  useTimelineEvent(timeline, 'on-click', (data) =\u003e {\n    console.log('Timeline clicked:', data);\n  });\n\n  useTimelineEvent(timeline, 'on-select-change', (data) =\u003e {\n    console.log('Selection changed:', data);\n  });\n\n  return \u003cTimelineCanvas timeline={timeline} /\u003e;\n};\n```\n\n### React Integration\n\nThe component uses custom hooks for timeline management:\n\n- `useTimeline`: Manages the timeline instance and its lifecycle\n  - Creates and initializes the timeline\n  - Handles cleanup on component unmount\n  - Provides access to the timeline instance\n\n- `useTimelineEvent`: Handles event subscriptions and cleanup\n  - Manages event listener lifecycle\n  - Automatically cleans up listeners on unmount\n\nThe component automatically handles cleanup and destruction of the timeline instance when unmounted.\n\n### Event Structure\n\nEvents in the timeline follow this structure:\n\n```typescript\ntype TimelineEvent = {\n  id: string;             // Unique identifier\n  from: number;           // Start timestamp\n  to?: number;            // End timestamp (optional for point events)\n  axisId: string;         // ID of the axis this event belongs to\n  trackIndex: number;     // Index in the axis track\n  renderer?: AbstractEventRenderer; // Optional custom renderer\n  color?: string;         // Optional event color\n  selectedColor?: string; // Optional selected state color\n};\n```\n\n### Direct TypeScript Usage\n\nThe Timeline class can be used directly in TypeScript without React. This is useful for integrating with other frameworks or vanilla JavaScript applications:\n\n```typescript\nimport { Timeline } from '@gravity-ui/timeline';\n\nconst timestamp = Date.now();\n\n// Create a timeline instance\nconst timeline = new Timeline({\n  settings: {\n    start: timestamp,\n    end: timestamp + 3600000, // 1 hour from now\n    axes: [\n      {\n        id: 'main',\n        tracksCount: 3,\n        top: 0,\n        height: 100\n      }\n    ],\n    events: [\n      {\n        id: 'event1',\n        from: timestamp + 1800000, // 30 minutes from now\n        to: timestamp + 2400000,   // 40 minutes from now\n        label: 'Sample Event',\n        axisId: 'main'\n      }\n    ],\n    markers: [\n      {\n        id: 'marker1',\n        time: timestamp + 1200000, // 20 minutes from now\n        label: 'Important Point',\n        color: '#ff0000',\n        activeColor: '#ff5252',\n        hoverColor: '#ff1744'\n      }\n    ],\n    sections: [\n      {\n        id: 'section1',\n        from: timestamp,\n        to: timestamp + 1800000, // First 30 minutes\n        color: 'rgba(33, 150, 243, 0.2)', // Light blue background\n        hoverColor: 'rgba(33, 150, 243, 0.3)'\n      }\n    ]\n  },\n  viewConfiguration: {\n    // Optional: customize view settings\n    zoomLevels: [1, 2, 4, 8, 16],\n    hideRuler: false,\n    showGrid: true\n  }\n});\n\n// Initialize with a canvas element\nconst canvas = document.querySelector('canvas');\nif (canvas instanceof HTMLCanvasElement) {\n  timeline.init(canvas);\n}\n\n// Add event listeners\ntimeline.on('on-click', (detail) =\u003e {\n  console.log('Timeline clicked:', detail);\n});\n\ntimeline.on('on-select-change', (detail) =\u003e {\n  console.log('Selection changed:', detail);\n});\n\n// Clean up when done\ntimeline.destroy();\n```\n\nThe Timeline class provides a rich API for managing the timeline:\n\n- **Event Management**:\n  ```typescript\n  // Add event listener\n  timeline.on('eventClick', (detail) =\u003e {\n    console.log('Event clicked:', detail);\n  });\n\n  // Remove event listener\n  const handler = (detail) =\u003e console.log(detail);\n  timeline.on('eventClick', handler);\n  timeline.off('eventClick', handler);\n\n  // Emit custom events\n  timeline.emit('customEvent', { data: 'custom data' });\n  ```\n\n- **Timeline Control**:\n  ```typescript\n  // Update timeline data\n  timeline.api.setEvents([\n    {\n      id: 'newEvent',\n      from: Date.now(),\n      to: Date.now() + 3600000,\n      label: 'New Event',\n      axisId: 'main',\n      trackIndex: 0\n    }\n  ]);\n\n  // Update axes\n  timeline.api.setAxes([\n    {\n      id: 'newAxis',\n      tracksCount: 2,\n      top: 0,\n      height: 80\n    }\n  ]);\n\n  // Update markers\n  timeline.api.setMarkers([\n    {\n      id: 'newMarker',\n      time: Date.now(),\n      label: 'New Marker',\n      color: '#00ff00',\n      activeColor: '#4caf50',\n      hoverColor: '#2e7d32'\n    }\n  ]);\n\n  // Update sections\n  timeline.api.setSections([\n    {\n      id: 'newSection',\n      from: Date.now(),\n      to: Date.now() + 1800000,\n      color: 'rgba(255, 193, 7, 0.2)', // Light amber background\n      hoverColor: 'rgba(255, 193, 7, 0.3)'\n    }\n  ]);\n\n  // Update view configuration (merges with current config)\n  timeline.api.setViewConfiguration({ hideRuler: true });\n  ```\n\n## Live Examples\n\nExplore interactive examples in our [Storybook](https://preview.gravity-ui.com/timeline/):\n\n- [Basic Timeline](https://preview.gravity-ui.com/timeline/?path=/story/timeline-events--basic) - Simple timeline with events and axes\n- [Endless Timeline](https://preview.gravity-ui.com/timeline/?path=/story/timeline-events--endless-timelines) - Endless timeline with events and axes\n- [Markers](https://preview.gravity-ui.com/timeline/?path=/story/timeline-markers--basic) - Timeline with vertical markers and labels\n- [Custom Events](https://preview.gravity-ui.com/timeline/?path=/story/timeline-events--custom-renderer) - Timeline with custom event rendering\n- [Integrations](https://preview.gravity-ui.com/timeline/?path=/story/integrations-gravity-ui--timeline-ruler) - RangeDateSelection, DragHandler, NestedEvents, Popup, List\n\n\n## Development\n\n### Storybook\n\nThis project includes Storybook for component development and documentation.\n\nTo run Storybook:\n\n```bash\nnpm run storybook\n```\n\nThis will start the Storybook development server on port 6006. You can access it at http://localhost:6006.\n\nTo build a static version of Storybook for deployment:\n\n```bash\nnpm run build-storybook\n```\n\n## License\n\nMIT\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgravity-ui%2Ftimeline","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fgravity-ui%2Ftimeline","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgravity-ui%2Ftimeline/lists"}