Dockable.PopoutTrigger
A button that pops the selected tab, or the whole tabset, out into a window, and docks it back from the window.
"A button, inside Dockable.TabSet, that pops the selected tab (or the whole tabset) out into a
window, and, in a window, docks it back into the main layout. It renders nothing when neither is
possible (popouts unsupported, the model refuses it, no tab): the engine decides
(engine.get("popout-mode-by", { nodeId })). It has no name of its own: give it an aria-label, or name it
from the state when the two actions need different names
(render={(props, state) => <button {...props} aria-label={state.mode === "dock" ? … : …} />})."
<Dockable.TabSet node={tabset}>
<div className="flex">
<Dockable.TabList<Types>>{(tab) => <Dockable.Tab node={tab}>{tab.label}</Dockable.Tab>}</Dockable.TabList>
<Dockable.PopoutTrigger aria-label="Pop out" className="data-[mode=dock]:rotate-180">
<ExternalLinkIcon aria-hidden />
</Dockable.PopoutTrigger>
</div>
<Dockable.TabSetContent />
</Dockable.TabSet>One aria-label names both actions. To name each one, read mode in a render function:
<Dockable.PopoutTrigger
render={(props, state) => (
<button {...props} aria-label={state.mode === "dock" ? "Dock back" : "Pop out"} />
)}
>
<ExternalLinkIcon aria-hidden />
</Dockable.PopoutTrigger>What a press runs
| mode | target | command |
|---|---|---|
"popout" | "tab" | tab.popout (engine.run("popout", { nodeId: tabId })): the tab moves into a new window layout, at its content area's screen rect |
"popout" | "tabset" | tabset.popout (engine.run("popout", { nodeId: tabsetId })): the whole tabset moves into a new window layout |
"dock" | either | engine.run("dock-back", { nodeId: id }): when the tab (or the tabset's tabs) is all its window holds, window.close, which docks every tab back and removes the window; otherwise a batch of tab.move into the main layout's active tabset (its first tabset when none is active) |
Every command goes through the model's middleware, so a veto stops it. A new window layout in the state is what opens the native window: the root's popout manager opens one for every window layout (see Dockable.Popout).
Props
PopoutTriggerProps<T> extends the common primitive props and the
<button> attributes.
| prop | type | default | description |
|---|---|---|---|
target | "tab" | "tabset" | "tab" | "tab" (default) pops out one tab; "tabset" pops out the whole tabset |
node | TabOf<T> | the tabset's selected tab | the tab to act on; default: the enclosing tabset's selected tab |
children | ReactNode | none | the button's content (an icon); the primitive renders no text of its own |
State
PopoutTriggerState:
| field | type | description |
|---|---|---|
mode | "popout" | "dock" | what a press does: open a window ("popout") or dock back into the main layout ("dock") |
target | "tab" | "tabset" | what it acts on: the selected tab (or node), or the whole tabset |
Data attributes and ARIA
| attribute | value |
|---|---|
data-layout-path | <tabset path>/button/popout, e.g. /ts0/button/popout |
data-mode | "popout" or "dock" |
data-target | "tab" or "tabset" |
aria-label | none of its own: pass yours, or set it from state.mode in render |
type | "button" |
Notes
- In the main layout it renders when
engine.can("popout", { nodeId: id })is true: popouts are supported (supportsPopout) andmodel.can("tab.popout", { tabId })(ortabset.popout) accepts it. The model refuses a tab whose resolvedpoppableis false (the built-in default: popout is opt-in, per tab or withdefaults.tab.poppable), a pinned tab, and a tabset that is empty or holds a tab that does not allow it. A middleware veto hides it too. - In a window (
model.is("node-in-window", { nodeId: id })) it always renders, in"dock"mode. - The engine actions it runs are public:
engine.run("popout", { nodeId })andengine.run("dock-back", { nodeId }), both returning aCommandResult.model.run("tab.popout", { tabId })works as well, anywhere.