Skip to content

SegmentBar ​

A whole split into parts, each as wide as its share, so a person compares them at a glance: the sections of a song, the steps of a job, what fills a budget.

tsx
import { SegmentBar } from '@adecore/ui';
tsx
import { useState } from 'react';
import { SegmentBar } from '@adecore/ui';

const SECTIONS = [
    { value: 12, label: 'Intro' },
    { value: 36, label: 'Verse' },
    { value: 28, label: 'Chorus' },
    { value: 4, label: 'Break' },
    { value: 30, label: 'Outro' }
];

const SCENES = [
    { start: 8, value: 30, label: 'Interview' },
    { start: 46, value: 22, label: 'B-roll' },
    { start: 80, value: 24, label: 'Credits' }
];

const BEATS = [
    ...Array.from({ length: 5 }, () => ({ value: 1, color: 'var(--positive)' })),
    { value: 1, current: true },
    ...Array.from({ length: 4 }, () => ({ value: 1 }))
];

const CONTEXT = [
    { value: 18, label: 'System', color: 'var(--text-faint)' },
    { value: 34, label: 'Files read', color: 'var(--status-running)' },
    { value: 21, label: 'Conversation', color: 'var(--positive)' }
];

export default function SegmentBarDemo() {
    const [picked, setPicked] = useState(1);

    return (
        <div className="flex w-full max-w-xl flex-col gap-6">
            <SegmentBar label="Sections" parts={SECTIONS.map((part, i) => ({ ...part, current: i === picked }))} onSelect={setPicked} />
            <SegmentBar label="Sections" parts={SECTIONS.slice(0, 3)} range={[0, 110]} />
            <SegmentBar label="Scenes" parts={SCENES} range={[0, 110]} />
            <SegmentBar label="Beats" parts={BEATS} size="sm" />
            <SegmentBar label="Context" parts={CONTEXT} range={[0, 128]} size="sm" />
        </div>
    );
}

The parts follow each other from 0, each as long as its value. Their bounds line up with anything drawn over the same stretch, such as the marks of a Waveform. A very narrow part may not show.

A part with its own start begins there, and the stretch before it stays empty. The parts still go in order.

range sets the stretch the bar shows, by default 0 to the end of the last part. A longer range leaves the rest empty, so bars side by side compare by eye. A shorter one shows the visible part of a zoomable timeline.

tsx
<SegmentBar label="Sections" parts={[{ value: 12, label: 'Intro' }, { value: 36, label: 'Verse', current: true }]} range={[0, 110]} />
<SegmentBar label="Scenes" parts={[{ start: 8, value: 30, label: 'Interview' }, { start: 46, value: 22, label: 'B-roll' }]} range={[0, 110]} />

The default size draws each label in its part, cut short where it does not fit, with a Tooltip for the whole label. size="sm" is a thin bar that shows labels only as a tooltip.

A part takes the theme's color, or any CSS color in color, such as var(--positive). current draws a part in the accent; color wins over it.

With onSelect every part is a button that passes its index in parts. Give every part a label then, as the button's name.

A screen reader hears the parts as a list, by label, with the current one marked. A bar without labels and onSelect is hidden from it, so name the whole on the element around it.

Props ​

PropTypeDefault
partsreadonly SegmentBarPart[]Required.
rangereadonly [from: number, to: number]0 up to the end of the last partThe stretch it shows, in the unit of the parts.
size'sm' | 'md''md'sm is thin without visible labels.
onSelect(index: number) => voidMakes every part a button.
labelstringThe name of the whole.
classNamestring
refRef<HTMLOListElement>

A SegmentBarPart has these fields:

FieldType
valuenumberRequired. Its length, in the unit of range.
startnumberWhere it starts, in the unit of range. Defaults to the end of the part before, or 0.
labelstringDrawn where there is room, and always read by a screen reader.
colorstringAny CSS color in place of the theme's.
currentbooleanThe part a person is at.

SegmentBarProps and SegmentBarPart are exported types.

Licenses and third-party notices are listed in each package.