Documentation
API: primitives (@fragiola/dockable-react)

Dockable.TabList

The tab strip of a tabset or border calls the child function per tab and moves focus between tabs with the arrow keys.

"The tabset's tab strip (role="tablist"). Calls the child function per tab. Arrow keys along the orientation (Left and Right swap in RTL, where a horizontal strip runs from the right), Home and End move focus between tabs; Enter or Space selects."

<Dockable.TabList<Types> aria-label={tabset.data?.name ?? "Tabs"} className="flex min-h-8 ps-1">
    {(tab) => (
        <Dockable.Tab node={tab} className="tab">
            {tab.component === "editor" && tab.data.dirty ? `${tab.label} •` : tab.label}
        </Dockable.Tab>
    )}
</Dockable.TabList>

The child function receives a plain TabOf<T>. Pass the model's registry as the type argument (<Dockable.TabList<Types>>): the list reads its tabs from the enclosing TabSet or Border through context, so it cannot infer T, and with the argument tab.data narrows on tab.component. Without it, T is AnyTypes and tab.data is unknown.

Render it inside a Dockable.TabSet (or a Dockable.Border). The engine measures it: the strip's rect is used for drop hit-testing, and the tabs that do not fit in it are hidden (tab overflow), for a Dockable.TabOverflowTrigger to list.

Props

TabListProps<T> extends the common primitive props.

proptypedefaultdescription
children(tab: TabOf<T>) => ReactNoderequiredrenders a tab button (a Dockable.Tab); pass the model's registry as the type argument (<Dockable.TabList<Types>>) and tab.data narrows on tab.component
orientation"horizontal" | "vertical""horizontal" ("vertical" in a start or end border)the direction the tabs are laid out in, for arrow key navigation; default horizontal (vertical in a start or end border)
overflowbooleantruetab overflow: the tabs that do not fit are hidden (the selected one stays), for a Dockable.TabOverflowTrigger to list; false keeps every tab, for a strip that wraps or scrolls

State

TabListState:

fieldtypedescription
orientation"horizontal" | "vertical"the orientation prop
dropTargetbooleanthe current drag would drop into this tab strip
dropIndexnumber | undefinedwhile it is the drop target: the insertion index in the strip
overflowingbooleansome tabs do not fit, so they are hidden (list them with Dockable.TabOverflowTrigger)

Data attributes and ARIA

attributevalue
data-layout-path<tabset path>/tabstrip, e.g. /ts0/tabstrip
data-orientation"horizontal" or "vertical"
data-drop-targetthe current drag would drop into this tab strip
data-drop-indexwhile it is the drop target: the insertion index among the tabset's children
data-overflowingsome tabs do not fit, so they are hidden
role"tablist"
aria-orientationthe orientation
aria-keyshortcutsthe focusNextTabset/focusPreviousTabset bindings, when set

Name it with aria-label (from the tabset's data, when the app keeps a name there).

Styling notes

  • Keep it at least ~30px tall at the top of the layout, or lower the global edgeDockMargin: the layout's top-edge docking band is 10px (gap 11).
  • No structural style: layout and wrapping are yours. Give the list a size the tabs can overflow (flex: 1; min-width: 0 in a header row, overflow: hidden): tab overflow compares the tabs' sizes, plus the list's gap, with the list's inner size (Tab overflow).

In a border

Dockable.TabList works the same inside Dockable.Border: its path is /border/<location>/tabstrip, and it runs vertically in a start or end border. The border's strip (Dockable.Border) is what the engine measures there, not the tab list.