Skip to content

Latest commit

 

History

History
471 lines (384 loc) · 15.9 KB

plugin-timeline.mdx

File metadata and controls

471 lines (384 loc) · 15.9 KB
title Plugin Timeline

import { InteractiveDemo } from '@/app/components/InteractiveDemo'; import { PluginLoader } from '@/app/components/PluginLoader';

Timeline component with vertical, horizontal, and Gantt-style layouts for displaying events and project timelines.

Installation

npm install @object-ui/plugin-timeline

<PluginLoader plugins={['timeline']}>

Interactive Examples

Vertical Timeline

Horizontal Timeline

Gantt-Style Timeline

Usage

Basic Usage

// Import once in your app entry point
import '@object-ui/plugin-timeline'
import type { TimelineSchema } from '@object-ui/types'

// Use in schemas - Vertical Timeline
const schema: TimelineSchema = {
  type: 'timeline',
  variant: 'vertical',
  items: [
    {
      time: '2024-01-15',
      title: 'Project Started',
      description: 'Kickoff meeting',
      variant: 'success'
    }
  ]
}

Features

  • Three Layout Variants: Vertical, horizontal, and Gantt-style layouts
  • Customizable Markers: Color-coded markers with icon support
  • Date Formatting: Multiple date format options
  • Gantt Charts: Project timeline visualization with task bars
  • Time Scales: Hour, day, week, month, quarter, or year scales for Gantt view
  • Lightweight: Pure CSS and React components

Schema API

Timeline Schema

{
  type: 'timeline',
  variant?: 'vertical' | 'horizontal' | 'gantt',
  items?: (TimelineFeedItem | TimelineGanttItem)[],
  dateFormat?: 'short' | 'long' | 'iso',
  // Gantt-specific
  scale?: 'hour' | 'day' | 'week' | 'month' | 'quarter' | 'year',
  rowLabel?: string,
  minDate?: string,
  maxDate?: string,
  className?: string
}

Timeline Item (Vertical/Horizontal)

Exported as TimelineFeedItem. These seven keys are the whole item — title is required.

{
  time?: string,             // ISO date string
  title: string,
  description?: string,
  variant?: 'default' | 'success' | 'warning' | 'danger' | 'info',
  icon?: string,             // Emoji or text
  content?: SchemaNode | SchemaNode[],  // Custom content below the description
  className?: string
}

Gantt Item

Exported as TimelineGanttItem (a row) and TimelineGanttItemBar (one bar). label is required; a row with no bars yet may omit items.

{
  label: string,             // Row label
  items?: Array<{
    title?: string,
    startDate?: string | number | Date,  // ISO date string, or epoch milliseconds
    endDate?: string | number | Date,
    variant?: 'default' | 'success' | 'warning' | 'danger' | 'info'
  }>
}

A bar whose dates cannot be drawn is not a validation failure: the chart shows its "unusable date range" message for it instead.

A date-only endDate is inclusive: the bar runs through the end of that day. So 2024-01-01 to 2024-01-31 fills January, and a bar that starts and ends on the same day is one day wide. An endDate with a time part, or given as a number or a Date, is an instant, and the bar ends exactly there.

`variant` decides which of the two shapes `items` holds — `vertical` (the default) and `horizontal` draw timeline items, `gantt` draws gantt rows. Validation judges every item against the shape its `variant` selects, so a gantt row on a vertical timeline, or a timeline item on a gantt timeline, is refused instead of drawing an empty entry. TypeScript cannot see this pairing (it spans the parent's `variant`), so it is caught when the schema is validated rather than at compile time. Items composed by an object-bound timeline also carry colour, group, status chips and a start/end range. Those are internal to the renderer, not item keys you author: `TimelineFeedItem` does not declare them, and the strict authoring schema in `@object-ui/types/zod` refuses them by name. Put a date in `time`, not `date`.

Properties

Property Type Default Description
variant string 'vertical' Timeline layout: vertical, horizontal, or gantt
items array [] Array of timeline items
dateFormat string 'short' Date formatting: short, long, or iso
scale string 'month' Gantt axis bucket: hour, day, week, month, quarter, or year
rowLabel string 'Items' Label for Gantt rows
minDate string auto Override min date for Gantt (YYYY-MM-DD)
maxDate string auto Override max date for Gantt (YYYY-MM-DD)
className string '' Additional Tailwind CSS classes

Every key above is declared on the exported TimelineSchema, so an editor completes them and a wrong value is a type error. scale is the canonical axis key and the only one — it is @objectstack/spec's ui/TimelineConfig.json spelling and the one the renderer reads.

`timeScale`, the pre-spec spelling of `scale`, is **retired** (objectui#6355). It is no longer read, and it is no longer authorable: the key is tombstoned on `TimelineSchema`, so writing it is a type error and a validation failure rather than a chart that quietly falls back to the `month` default. Rename the key to `scale` — the accepted values are unchanged. `events`, `orientation` and `position` are **retired** (objectui#6170). Nothing ever read them — a timeline authored with `events` rendered an empty rail — and they are no longer authorable: each is tombstoned on `TimelineSchema`, so writing one is a type error and a validation failure. Write `items` instead of `events` (each entry `{ time, title, … }`; the date goes in `time`, not `date`) and `variant` instead of `orientation`. `position` has no replacement: the vertical rail is always drawn on the left.

Object-bound timeline (object-timeline)

object-timeline fetches records from objectName and draws one entry per record, from the fields its timeline config names. An authored node takes its props in the properties bag, where @objectstack/spec's ComponentPropsMap['object-timeline'] row declares them:

{
  "type": "object-timeline",
  "properties": {
    "objectName": "project_task",
    "timeline": { "startDateField": "start_date", "titleField": "name" },
    "navigation": { "mode": "drawer" }
  }
}

Where the entries come from

The first record source present wins: items (authored entries, drawn as written), then data (pre-fetched records), then a bind path, then the query on objectName. Only the query is a fetch, so objectName is optional: a timeline drawn from items, data or a bind path needs none to draw (entry-click navigation still reads it, below), and a node-level dataSource binding can supply the object instead. Records, from data, a bind path or the query, are composed into entries through the field bindings below; authored items are not.

Prop What the renderer does with it
timeline Names the record fields an entry is drawn from: startDateField (its date), endDateField (the end printed after it), titleField, groupByField (the field whose value heads each group; without it, entries group into date buckets) and colorField (the marker colour: the field's option colour for the value, or the value itself when it is a colour literal). Each outranks the flat key of the same binding. scale sets the gantt axis unit.
mapping { title, date, description, variant } field names. title and date are read where the timeline block leaves the binding unset, ahead of the flat keys. variant names the field whose value picks the marker colour (the record's own variant field without it); on the vertical rail a colour from colorField outranks it.
descriptionField The field an entry's description is read from (default description); mapping.description outranks it.
limit The row cap, lowered to $top (default 100). A value that is not a positive integer is ignored with a console warning.
filter / sort Lowered to $filter / $orderby on the query, so they do nothing when items or data supply the entries. The rail draws entries composed from records by start date, earliest first, whatever sort says.
dateFormat short (the default) or long, both locale-formatted, or iso.
rowLabel, minDate, maxDate The gantt's row-column header and its pinned axis start and end, read only by the gantt branch, which on this block draws authored items. An empty minDate / maxDate is not honoured; a value that is not a date, or a start after the end, refuses the chart with a diagnostic.

Entry-click navigation

navigation is what an entry click opens — the spec's NavigationConfig by reference, ViewNavigationConfig in @object-ui/types, the type ObjectGridSchema.navigation uses: mode (page, drawer, modal, split, popover, new_window or none) with size, openNewTab and preventNavigation.

Member On an entry click
mode: 'drawer' / 'modal' / 'popover' Opens the entry's record in that overlay
mode: 'new_window', or openNewTab: true Opens the record page in a new tab; openNewTab outranks every mode except none. On a timeline that names no objectName there is no record page: the tab opens at a slash and the entry's id alone (its _id when it has no id, undefined when it has neither)
mode: 'page', or a block without mode (the spec's page default) Opens the record page of the timeline's objectName through the record navigator the host publishes (the console publishes one on its custom pages, record pages and list views); under a host that publishes none, such as an embedded renderer, or on a timeline that names no objectName, it opens nothing
preventNavigation: true, or mode: 'none' Opens nothing, whatever else the block says
size Sets the overlay width (sm … full, or auto for the default)

A click handler from a parent view outranks the whole key.

With the key absent a click opens nothing: unlike the board and the calendar, this renderer supplies no drawer default. `split` opens nothing on this block either, because the timeline gives the split shell no main panel.

Variants

Marker Variants

Available marker/bar colors:

  • default - Gray
  • success - Green
  • info - Blue
  • warning - Yellow
  • danger - Red
const item = {
  time: '2024-01-15',
  title: 'Important Milestone',
  variant: 'success'  // Green marker
}

Examples

Product Roadmap

import type { TimelineSchema } from '@object-ui/types'

const roadmap: TimelineSchema = {
  type: 'timeline',
  variant: 'horizontal',
  dateFormat: 'long',
  items: [
    {
      time: '2024-01-01',
      title: 'Alpha Release',
      description: 'Internal testing phase',
      variant: 'info',
      icon: '🔬'
    },
    {
      time: '2024-03-01',
      title: 'Beta Launch',
      description: 'Limited public beta',
      variant: 'warning',
      icon: '🚀'
    },
    {
      time: '2024-06-01',
      title: 'Public Launch',
      description: 'General availability',
      variant: 'success',
      icon: '🎉'
    }
  ]
}

Project Schedule (Gantt)

import type { TimelineSchema } from '@object-ui/types'

const schedule: TimelineSchema = {
  type: 'timeline',
  variant: 'gantt',
  scale: 'week',
  rowLabel: 'Teams',
  items: [
    {
      label: 'Design Team',
      items: [
        {
          title: 'Wireframes',
          startDate: '2024-01-01',
          endDate: '2024-01-14',
          variant: 'info'
        },
        {
          title: 'Mockups',
          startDate: '2024-01-15',
          endDate: '2024-01-31',
          variant: 'success'
        }
      ]
    },
    {
      label: 'Engineering',
      items: [
        {
          title: 'Development',
          startDate: '2024-02-01',
          endDate: '2024-03-31',
          variant: 'warning'
        }
      ]
    }
  ]
}

Company History

import type { TimelineSchema } from '@object-ui/types'

const history: TimelineSchema = {
  type: 'timeline',
  variant: 'vertical',
  dateFormat: 'long',
  items: [
    {
      time: '2020-01-01',
      title: 'Company Founded',
      description: 'Started in a garage with 2 founders',
      variant: 'success',
      icon: '🏢'
    },
    {
      time: '2021-06-01',
      title: 'Series A Funding',
      description: 'Raised $5M from VCs',
      variant: 'info',
      icon: '💰'
    },
    {
      time: '2022-12-01',
      title: '10,000 Customers',
      description: 'Reached major milestone',
      variant: 'success',
      icon: '🎯'
    },
    {
      time: '2024-01-01',
      title: 'Series B Funding',
      description: 'Raised $20M to expand globally',
      variant: 'warning',
      icon: '🌍'
    }
  ]
}

Date Formatting

The timeline component supports three date formats:

import type { TimelineSchema } from '@object-ui/types'

const short: TimelineSchema['dateFormat'] = 'short'  // "1/15/2024"
const long: TimelineSchema['dateFormat'] = 'long'    // "January 15, 2024"
const iso: TimelineSchema['dateFormat'] = 'iso'      // "2024-01-15"

A date-only value (2024-01-15) names a calendar day, so every face prints that day in every viewer's time zone, and the object timeline buckets and sorts it as that day. A value with a time part is an instant: short and long print its day in the viewer's zone, and iso prints its UTC day.

Without a groupByField, the object timeline groups entries into date buckets (Earlier, Today, Tomorrow, This week, Next week, Later, No date). "This week" starts on the first day of the week of the user's display locale, the day the calendar's weeks start on: Sunday under en-US, Monday under en-GB or zh-CN.

Time Scales (Gantt)

For Gantt-style timelines, choose the appropriate time scale:

import type { TimelineSchema } from '@object-ui/types'

const shortProject: TimelineSchema['scale'] = 'day'    // short projects
const mediumProject: TimelineSchema['scale'] = 'week'  // medium projects
const longProject: TimelineSchema['scale'] = 'month'   // long projects

A date-only bar date, minDate or maxDate (2024-01-15) names a calendar day, so the axis headers, the bar positions and the bar tooltips all place it on that day in every viewer's time zone. A value with a time part is an instant and sits at its hour in the viewer's day. A plan with no bar dates and no pinned range shows a one-day axis on the viewer's today.

The headers and the bars share one axis. It runs from the start of the unit that holds the first date to the end of the unit that holds the last. Each header is as wide as its unit is long: hour, day and week columns are equal, and a month is as wide as its days. So a bar lines up under the header of the unit it starts in.

Customization

Custom Icons

const item = {
  time: '2024-01-15',
  title: 'Milestone',
  icon: '🎯',  // Any emoji or short text
  variant: 'success'
}

Custom Colors

Use Tailwind CSS classes for additional styling:

import type { TimelineSchema } from '@object-ui/types'

const schema: TimelineSchema = {
  type: 'timeline',
  variant: 'vertical',
  className: 'max-w-4xl mx-auto',
  items: [
    { time: '2024-01-15', title: 'Milestone', variant: 'success' }
  ]
}

TypeScript Support

import type { TimelineSchema } from '@object-ui/types'

const timelineSchema: TimelineSchema = {
  type: 'timeline',
  variant: 'vertical',
  items: [
    {
      time: '2024-01-15',
      title: 'Event',
      description: 'Description',
      variant: 'success'
    }
  ]
}

Related Documentation