Splitter
Accessible, unstyled resizable panel groups following the WAI-ARIA window splitter pattern.
|
|
| Package |
@vizejs/ui/splitter |
| Maturity |
stable |
| Own the source |
vize lib pull splitter |
| Requires |
collection, context, controllable-state, id |
| Aliases |
resizable panels, split view, split pane, window splitter, panel group |
| Covers |
WAI-ARIA window splitter pattern, react-resizable-panels, Reka UI Splitter, Ark UI Splitter |
Usage
import { SplitterGroup, SplitterHandle, SplitterPanel } from "@vizejs/ui/splitter";
Or copy the source into your project with vize lib pull splitter (see Source Distribution).
API
SplitterGroup
Source: src/families/layout/splitter/splitter-group.vue
Props
| Prop |
Type |
Default |
Description |
id |
string | null |
undefined |
Consumer-owned group id. null and undefined select a deterministic fallback. |
layout |
SplitterLayout |
undefined |
Controlled panel sizes in percent (v-model:layout). undefined selects uncontrolled behavior, which also lets useSplitterPersistence load a stored layout after mount. |
defaultLayout |
SplitterLayout |
undefined |
Initial sizes by panel index. Panels without an entry use their defaultSize. |
orientation |
SplitterOrientation |
"horizontal" |
Layout axis. "horizontal" places panels side by side with vertical separators. |
dir |
SplitterDirection |
undefined |
Reading direction used to map horizontal arrow keys and pointer movement. undefined inherits DirectionProvider/LocaleProvider, then "ltr". |
disabled |
boolean |
false |
Disable pointer and keyboard resizing while keeping the current layout. |
keyboardStep |
number |
10 |
Percent moved by one arrow key press on a handle. |
Events
| Event |
Payload |
Description |
update:layout |
[layout: SplitterLayout] |
Fired with the next layout whenever sizes change. |
resize |
[layout: SplitterLayout, reason: SplitterResizeReason] |
Fired after a distinct layout change with the input that caused it. |
resizeStart |
[handleIndex: number] |
Fired when a pointer drag starts on a handle. |
resizeEnd |
[layout: SplitterLayout] |
Fired when a pointer drag ends. |
Slots
| Slot |
Slot props |
Description |
default |
SplitterGroupSlotState |
SplitterPanel and SplitterHandle children. Receives the resolved layout and drag state. |
Exposed
| Member |
Type |
Description |
element |
HTMLDivElement | null |
Rendered group element. |
id |
string |
Group base id. |
layout |
SplitterLayout |
Resolved layout. |
setLayout |
(layout: SplitterLayout) => boolean |
Request a new layout. Invalid lengths are rejected and sizes are clamped to constraints. |
reset |
() => boolean |
Restore defaultLayout or the panel defaults. |
SplitterHandle
Source: src/families/layout/splitter/splitter-handle.vue
Props
| Prop |
Type |
Default |
Description |
id |
string | null |
undefined |
Consumer-owned separator id. |
disabled |
boolean |
false |
Disable this handle while keeping the rest of the group resizable. |
order |
number |
undefined |
Deterministic order for conditionally rendered handles. |
ariaLabel |
string |
undefined |
Accessible name, for example "Resize sidebar". |
ariaLabelledby |
string |
undefined |
Space-separated ids that label the separator. |
Slots
| Slot |
Slot props |
Description |
default |
SplitterHandleSlotState |
Optional grip content. Receives the value, orientation, and drag state. |
Exposed
| Member |
Type |
Description |
element |
HTMLDivElement | null |
Rendered separator element. |
value |
number |
Size in percent of the panel before the handle. |
focus |
(options?: FocusOptions) => void |
Move focus to the separator. |
SplitterPanel
Source: src/families/layout/splitter/splitter-panel.vue
Props
| Prop |
Type |
Default |
Description |
id |
string | null |
undefined |
Consumer-owned panel id, also used by aria-controls on the adjacent handle. |
defaultSize |
number |
undefined |
Initial size in percent. Give every panel a size for exact server rendering. |
minSize |
number |
0 |
Smallest expanded size in percent. |
maxSize |
number |
100 |
Largest size in percent. |
collapsible |
boolean |
false |
Allow the panel to snap to collapsedSize when dragged past half of minSize. |
collapsedSize |
number |
0 |
Size in percent while collapsed. |
order |
number |
undefined |
Deterministic order for conditionally rendered panels. |
Events
| Event |
Payload |
Description |
collapse |
[] |
Fired when the panel snaps to its collapsed size. |
expand |
[] |
Fired when a collapsed panel grows back to at least its minimum size. |
resize |
[size: number, previous: number] |
Fired with the panel's new size whenever it changes. |
Slots
| Slot |
Slot props |
Description |
default |
SplitterPanelSlotState |
Panel content. Receives the current size and collapsed state. |
Exposed
| Member |
Type |
Description |
element |
HTMLDivElement | null |
Rendered panel element. |
id |
string |
Panel id. |
size |
number |
Current size in percent. |
collapsed |
boolean |
Whether the panel is collapsed. |
collapse |
() => boolean |
Collapse a collapsible panel, remembering its size. |
expand |
() => boolean |
Restore a collapsed panel to its remembered or minimum size. |
resize |
(size: number) => boolean |
Resize the panel toward size percent through its adjacent handle. |
Behavior
Normative state x input -> outcome table for splitter-group.vue,
splitter-panel.vue, and splitter-handle.vue (@vizejs/ui/splitter), plus
the useSplitterPersistence hook and the pure layout helpers. Every row is
proven by the named test in splitter.test.ts, splitter-layout.test.ts, or
splitter-ssr.test.ts; compile-only guarantees live in
splitter.types.test-d.ts.
The handle follows the WAI-ARIA APG window splitter pattern: a focusable
role="separator" whose aria-valuenow is the size of the panel before it
(its primary pane, named by aria-controls) and whose aria-orientation is
perpendicular to the group axis. Sizes are percentages of the group and the
only inline styles are the flex declarations that carry them.
| ID |
State |
Input |
Outcome |
Evidence |
| S1 |
default |
render |
flex group, flex-grow sizes, separator value/min/max/controls, perpendicular orientation |
renders the APG window splitter contract with flex-carried sizes |
| S2 |
focused handle |
ArrowLeft/Right, Home, End |
resize by keyboardStep inside min/max; Home/End jump to the primary pane's bounds |
arrow keys resize by the keyboard step within constraints; Home and End jump |
| S3 |
vertical group / RTL |
ArrowUp/Down, ArrowLeft |
vertical groups use block arrows; RTL flips inline arrows |
vertical groups use ArrowUp/ArrowDown and RTL flips horizontal arrows |
| S4 |
collapsible primary pane |
Enter, Home, ArrowRight |
Enter collapses and restores the remembered size; collapsed panes grow straight to minSize |
collapsible panels snap closed past half their minimum and Enter toggles them |
| S5 |
pointer drag |
pointerdown, move, up |
sizes follow the pointer from the drag origin, clamp to constraints, emit start/end |
pointer drags resize from the drag origin and report start and end |
| S6 |
disabled group |
keys, pointer |
no resize, separator leaves the tab order and reports aria-disabled |
disabled groups and handles ignore keyboard and pointer input |
| S7 |
controlled layout |
resize, invalid layout |
update events fire while the parent owns sizes; wrong panel counts fall back to defaults |
controlled layouts wait for the parent and invalid layouts fall back to defaults |
| S8 |
exposed instances |
setLayout, collapse, expand, resize, reset |
typed imperative layout control with validation |
exposes group and panel controls for programmatic layout changes |
| S9 |
nested groups |
keys on the inner handle |
each group owns its own panels and handles |
nested groups resize independently |
| S10 |
useSplitterPersistence |
mount, resize, malformed storage |
defaults render first, the stored layout loads after mount, changes are written back |
useSplitterPersistence loads after mount and writes later layouts |
| S11 |
missing provider |
setup |
panels and handles fail closed with the shared context diagnostic |
compound parts require a matching group provider |
| S12 |
layout helpers |
validate, default, clamp, resize |
remainder sharing, clamping, cascading resize, and midpoint collapse snapping |
splitter-layout.test.ts |
| S13 |
SSR and hydration |
isolated render/mount |
byte-identical exact sizes, controlled layouts before registration, hydration without warnings |
splitter-ssr.test.ts |
Notes
- For exact server rendering give every panel a
defaultSize, or pass
defaultLayout/layout to the group; unsized panels share the remainder
once all panels have registered.
- Resizing only moves the boundary between the handle's neighbours, cascading
through further panels when a neighbour reaches its minimum.