|
|
5 months ago | |
|---|---|---|
| .. | ||
| components | 5 months ago | |
| README.md | 6 months ago | |
| exports.ts | 6 months ago | |
| index.ts | 6 months ago | |
| langs.ts | 5 months ago | |
| splitter-provider.svelte.ts | 5 months ago | |
| types.ts | 6 months ago | |
README.md
Splitter
Resizable panels separated by draggable handles. Supports horizontal/vertical orientation, collapsible panels, keyboard resize, double-click collapse, and ARIA separator semantics.
Anatomy
<Splitter.Provider>
<Splitter.Panel panelId="left" defaultSize={30}>Left panel</Splitter.Panel>
<Splitter.ResizeTrigger panelBefore="left" panelAfter="right" />
<Splitter.Panel panelId="right" defaultSize={70}>Right panel</Splitter.Panel>
</Splitter.Provider>
Panels and resize triggers are placed in order. Each trigger references the two panels it sits between.
Parts
| Part | Element | Description |
|---|---|---|
Provider |
<div> |
Root flex container. Manages panel sizes, orientation, resize logic. |
Panel |
<div> |
Resizable panel. Flex-basis controlled by percentage size. |
ResizeTrigger |
<div> |
Drag handle between panels. ARIA separator. Pointer + keyboard resize. |
ARIA
| Part | Attribute | Value |
|---|---|---|
| ResizeTrigger | role |
separator |
| ResizeTrigger | aria-orientation |
horizontal | vertical |
| ResizeTrigger | aria-valuenow |
Current size of the panel before the trigger (%) |
| ResizeTrigger | aria-valuemin |
Minimum size of the panel before (%) |
| ResizeTrigger | aria-valuemax |
Maximum size of the panel before (%) |
| ResizeTrigger | tabindex |
0 (focusable) | -1 (disabled) |
Data Attributes
| Part | Attribute | Values |
|---|---|---|
| Provider | data-splitter |
Always present |
| Provider | data-orientation |
horizontal | vertical |
| Provider | data-disabled |
Present when disabled |
| Panel | data-splitter-panel |
Always present |
| Panel | data-panel-id |
The panel's identifier |
| Panel | data-state |
collapsed | expanded |
| Panel | data-orientation |
horizontal | vertical |
| ResizeTrigger | data-splitter-resize-trigger |
Always present |
| ResizeTrigger | data-state |
dragging | hover | inactive |
| ResizeTrigger | data-orientation |
horizontal | vertical |
| ResizeTrigger | data-disabled |
Present when disabled |
| ResizeTrigger | data-dragging |
Present during drag |
Keyboard
| Key | Action |
|---|---|
ArrowRight |
Grow panel before trigger (horizontal, LTR) |
ArrowLeft |
Shrink panel before trigger (horizontal, LTR) |
ArrowDown |
Grow panel before trigger (vertical) |
ArrowUp |
Shrink panel before trigger (vertical) |
Home |
Minimize panel before trigger to its minSize |
End |
Maximize panel before trigger to its maxSize |
Enter |
Toggle collapse/expand on collapsible panel before trigger |
Arrow key step is configurable via keyboardStep (default 1%).
Behavior
Drag resize
Pointer capture ensures smooth dragging even when the cursor leaves the handle. Panel sizes update proportionally to cursor displacement.
Collapsible panels
Panels with collapsible can collapse to collapsedSize (default 0%). Collapse is triggered by:
- Dragging past the collapse threshold (collapsedSize + 1%)
- Double-clicking the adjacent resize trigger
- Pressing Enter on the adjacent resize trigger
- Programmatically via the provider's
collapsePanel/expandPanelmethods
Size constraints
Each panel can define minSize and maxSize (percentages). The resize logic clamps values and prevents invalid states.
Panel state in snippets
Panel exposes collapsed and size to child/children snippets:
<Splitter.Panel panelId="sidebar" defaultSize={25} collapsible>
{#snippet children({ collapsed, size })}
{#if collapsed}
<button>Expand</button>
{:else}
<nav>Sidebar ({Math.round(size)}%)</nav>
{/if}
{/snippet}
</Splitter.Panel>
Usage
Basic horizontal
<script>
import { Splitter } from '$soma/components';
</script>
<Splitter.Provider>
<Splitter.Panel panelId="a" defaultSize={50}>Panel A</Splitter.Panel>
<Splitter.ResizeTrigger panelBefore="a" panelAfter="b" />
<Splitter.Panel panelId="b" defaultSize={50}>Panel B</Splitter.Panel>
</Splitter.Provider>
Vertical
<Splitter.Provider orientation="vertical">
<Splitter.Panel panelId="top" defaultSize={30}>Top</Splitter.Panel>
<Splitter.ResizeTrigger panelBefore="top" panelAfter="bottom" />
<Splitter.Panel panelId="bottom" defaultSize={70}>Bottom</Splitter.Panel>
</Splitter.Provider>
Three panels
<Splitter.Provider>
<Splitter.Panel panelId="left" defaultSize={25} minSize={15}>Left</Splitter.Panel>
<Splitter.ResizeTrigger panelBefore="left" panelAfter="center" />
<Splitter.Panel panelId="center" defaultSize={50} minSize={20}>Center</Splitter.Panel>
<Splitter.ResizeTrigger panelBefore="center" panelAfter="right" />
<Splitter.Panel panelId="right" defaultSize={25} minSize={15}>Right</Splitter.Panel>
</Splitter.Provider>
Collapsible sidebar
<Splitter.Provider>
<Splitter.Panel panelId="sidebar" defaultSize={25} minSize={15} collapsible collapsedSize={0}>
Sidebar content
</Splitter.Panel>
<Splitter.ResizeTrigger panelBefore="sidebar" panelAfter="main" />
<Splitter.Panel panelId="main" defaultSize={75}>Main content</Splitter.Panel>
</Splitter.Provider>
Resize callbacks
<Splitter.Provider
onResize={(sizes) => console.log('resizing:', sizes)}
onResizeEnd={(sizes) => console.log('done:', sizes)}
>
...
</Splitter.Provider>
Comparison with reference libraries
| Feature | Ark UI | Radix Vue | react-resizable-panels | Soma |
|---|---|---|---|---|
| Parts | Root, Panel, ResizeTrigger, Indicator | Group, Panel, ResizeHandle | PanelGroup, Panel, PanelResizeHandle | Provider, Panel, ResizeTrigger |
| Orientation | Yes | Yes | Yes | Yes |
minSize/maxSize |
Yes (via panels config) | Yes (per panel) | Yes (per panel) | Yes (per panel) |
| Collapsible panels | Yes | Yes | Yes | Yes |
collapsedSize |
Yes | Yes | Yes | Yes |
| Collapse/expand methods | Yes | Yes | Yes | Yes |
| Double-click collapse | No | No | Yes | Yes |
| Keyboard arrows | Yes | Yes | Yes | Yes |
| Home/End keys | Yes | Yes | Yes | Yes |
| Enter toggle collapse | Yes | Yes | Yes | Yes |
| ARIA separator | Yes | Yes | Yes | Yes |
aria-valuenow/min/max |
Yes | Yes | Yes | Yes |
data-state (drag/hover) |
Yes | Yes (drag/hover/inactive/collapsed/expanded) | Partial | Yes (dragging/hover/inactive + collapsed/expanded on panel) |
data-dragging |
Yes | No | Yes | Yes |
onResize callback |
Yes | Yes (@layout) | Yes | Yes |
onResizeEnd callback |
Yes | No | No | Yes |
keyboardStep |
Yes | Yes | No (fixed) | Yes |
dir (RTL) |
No | No | No | Yes |
disabled (global) |
No | No | No | Yes |
disabled (per trigger) |
Yes | Yes | Yes | Yes |
| Pointer capture | Yes | Yes | Yes | Yes |
| Panel state in slots/snippets | No | Yes (isCollapsed) | No | Yes (collapsed, size) |
| Layout persistence | No | Yes (autoSaveId) | Yes (storage) | No (consumer responsibility) |
| Multiple size units | No | No | Yes (px, em, etc.) | No (% only) |
| Hit area margins | Yes | Yes | Yes | No (CSS padding on trigger) |