CentercodeDitto
DittoPorygonAPI

TimelinePanel

Togglable timeline sidebar panel for hub pages with URL bookmark support

Overview

TimelinePanel provides a right-side panel for displaying ScopeTimeline on hub pages. It integrates with URL query parameters for bookmarkable state and PropertyPanel for consistent panel behavior including pin, expand, and minimize controls.

Key Features

  • URL bookmark support via ?timeline=open query parameter
  • Integrates with PropertyPanel for pin/expand/minimize controls
  • Optional always-open mode for permanent timeline display
  • Companion TimelineToggleButton component for PageHeader
  • useTimelinePanel hook for programmatic control
  • Automatic URL sync on browser back/forward navigation

Architecture

URL-Based State Management

TimelinePanel uses URL state for persistence and PropertyPanel for consistent panel behavior.

The panel state is stored in the URL via the ?timeline=open query parameter. This means users can bookmark pages with the timeline open, share links that open directly to the timeline view, and use browser back/forward to toggle the panel. The component automatically syncs with URL changes.

Panel closed:/program/project/hub
Panel open:/program/project/hub?timeline=open

Layout Integration

The panel integrates with LayoutShell's rightPanel prop for proper layout coordination.

TimelinePanel is designed to work with LayoutShell's rightPanel prop. The LayoutShell handles the panel container positioning while TimelinePanel manages its visibility and content. Use TimelineToggleButton in the PageHeader actions for a consistent toggle experience.

Module Exports

TimelinePanel

The main panel component.

Renders the timeline as a togglable right panel. Pass programId and/or projectId to filter timeline events by scope. Use alwaysOpen for permanent display.

TimelineToggleButton

Self-contained toggle button.

A button that reads state from URL and toggles the timeline panel on click. Styled as outline when closed and secondary when open. Use in PageHeader actions for consistent positioning.

// In PageHeader actions
<PageHeader
  title="Project Hub"
  actions={<TimelineToggleButton />}
/>

// With custom styling
<TimelineToggleButton className="hidden md:flex" />

useTimelinePanel

Hook for programmatic control.

Returns isOpen state and toggle/open/close functions for controlling the timeline panel programmatically. All operations sync with URL state.

import { useTimelinePanel } from '@/ui/components';

function MyComponent() {
  const { isOpen, toggle, open, close } = useTimelinePanel();

  // Check if timeline is currently open
  if (isOpen) {
    console.log('Timeline is visible');
  }

  // Toggle on button click
  <Button onClick={toggle}>Toggle Timeline</Button>

  // Open timeline programmatically
  <Button onClick={open}>Show Timeline</Button>

  // Close timeline programmatically
  <Button onClick={close}>Hide Timeline</Button>
}

Props Reference

TimelinePanel Props

PropTypeDefaultDescription
programIdstring-Program ID for filtering timeline events (required for program/project scope)
projectIdstring-Project ID for filtering timeline events (required for project scope)
alwaysOpenbooleanfalseWhen true, panel is always visible regardless of URL state

TimelineToggleButton Props

PropTypeDefaultDescription
classNamestring-Additional CSS classes for the button

useTimelinePanel Return Value

PropTypeDescription
isOpenbooleanWhether the timeline panel is currently open
toggle() => voidToggle the panel open/closed
open() => voidOpen the panel (no-op if already open)
close() => voidClose the panel (no-op if already closed)

Usage

import { TimelinePanel, TimelineToggleButton, useTimelinePanel } from '@/ui/components';

// In a hub page with LayoutShell
<LayoutShell
  pageHeader={{
    title: 'Project Hub',
    actions: <TimelineToggleButton />,
  }}
  rightPanel={<TimelinePanel programId={programId} projectId={projectId} />}
>
  {/* Page content */}
</LayoutShell>

// Always-open panel (no toggle behavior)
<TimelinePanel programId={programId} projectId={projectId} alwaysOpen />

// Programmatic control via hook
function MyComponent() {
  const { isOpen, toggle, open, close } = useTimelinePanel();

  return (
    <Button onClick={toggle}>
      {isOpen ? 'Hide Timeline' : 'Show Timeline'}
    </Button>
  );
}