Dockable.Panel
A tab's content panel, positioned by the engine over its tabset's content area and kept mounted when the tab moves.
"A tab's content panel (role="tabpanel"). Renders two portals:
- the panel element, into the panel layer of the tab's current layout; the engine positions it over the tabset's content area and shows or hides it (structural style only);
- the content (
children), into the tab's moveable element, which the engine moves into whichever panel element is current.
The content's position in the React tree never changes (it always lives under
Dockable.Panels), so moving a tab to another tabset or another window re-parents DOM without
remounting the content."
<Dockable.Panels<Types>>
{(tab) => (
<Dockable.Panel
node={tab}
scrollable={tab.component !== "chart"}
remountInWindow={tab.component === "video"}
className="bg-palette-base text-palette-contrast"
>
<TabContent tab={tab} />
</Dockable.Panel>
)}
</Dockable.Panels>Props
PanelProps<T> extends the common primitive props.
| prop | type | default | description |
|---|---|---|---|
node | TabOf<T> | required | the tab whose content this is |
children | ReactNode | none | the tab's content |
scrollable | boolean | true | the content element scrolls its overflow; false clips it |
remountInWindow | boolean | false | remount the content when the tab moves to another window (default: the same content moves, keeping its state). For content bound to its document (an iframe, a canvas context) |
Both are view options, not model attributes: decide them per tab from tab.component or
tab.data.
style (a value or a function) is accepted, but the keys the engine owns are dropped from
it, and from the style of a render element: position, inset, left, top, right,
bottom, width, height, display.
State
PanelState:
| field | type | description |
|---|---|---|
selected | boolean | the tab is its tabset's selected tab (model.is("tab-selected", { tabId })) |
visible | boolean | the panel is shown (selected, and not hidden by another tabset being maximized): engine.is("tab-panel-visible", { tabId: tab.id }) |
Data attributes and ARIA
| attribute | value |
|---|---|
data-layout-path | the tab's panel path: /ts0/t1 |
data-selected | the selected tab |
data-visible | shown |
id | engine.get("tab-panel-dom-id-by", { tabId: tab.id }), unique on the page (getTabPanelId(id, scope): dockable-<scope>tab-<id>) |
role | "tabpanel" |
aria-labelledby | its tab button's id |
aria-keyshortcuts | the focusTabToggle binding, when set |
tabIndex | -1 (focusable from code: Enter on the selected tab moves focus here) |
Inside it, the content sits in the moveable element: <div data-dockable-moveable>, sized
100% by 100%, with overflow: auto (or hidden when scrollable is false).
Structural style
position: absolute from the first render. The engine writes left, top, width, height
and display directly.
Behaviour
- A press inside makes its tabset active (
tabset.activate, through the model's middleware). - With
focusTabTogglebound, that key (anywhere in the content) returns focus to the tab button. remountInWindowremounts its content when the tab moves between windows (the content is keyed by its layout).
Styling notes
The panel is not a child of its tabset: the tabset's border-radius and overflow do not clip
it, and tabset state selectors do not reach it (gap 2). It paints over absolutely positioned
siblings that come before it in the root, so give overlays (the drop indicator) a z-index
(gap 12).