solid-tom-ui 1.0.10 → 1.0.14
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +246 -246
- package/dist/README.md +246 -246
- package/dist/components/avatar/avatar.js.map +1 -1
- package/dist/components/badge/badge.js.map +1 -1
- package/dist/components/breadcrumb/breadcrumb.js.map +1 -1
- package/dist/components/button/button.js.map +1 -1
- package/dist/components/carousel/carousel.js.map +1 -1
- package/dist/components/chat-bubble/chatBubble.js.map +1 -1
- package/dist/components/checkbox/checkbox.js.map +1 -1
- package/dist/components/collapse/collapse.js.map +1 -1
- package/dist/components/context-menu/context-menu.js.map +1 -1
- package/dist/components/context-menu/context-menu.store.js.map +1 -1
- package/dist/components/divider/divider.js.map +1 -1
- package/dist/components/dropdown/dropdown.js.map +1 -1
- package/dist/components/dropdown/dropdown.store.js.map +1 -1
- package/dist/components/float-button/float-button.js.map +1 -1
- package/dist/components/hover-3d-image/hover-3d-image.js.map +1 -1
- package/dist/components/image-preview/image-preview.js.map +1 -1
- package/dist/components/input/input.js.map +1 -1
- package/dist/components/input/input.utils.js.map +1 -1
- package/dist/components/input/variants/input-color.js.map +1 -1
- package/dist/components/input/variants/input-date.js.map +1 -1
- package/dist/components/input/variants/input-number.d.ts.map +1 -1
- package/dist/components/input/variants/input-number.js +1 -1
- package/dist/components/input/variants/input-number.js.map +1 -1
- package/dist/components/input/variants/input-otp.js.map +1 -1
- package/dist/components/input/variants/input-password.js.map +1 -1
- package/dist/components/input/variants/input-radio.js.map +1 -1
- package/dist/components/input/variants/input-range.js.map +1 -1
- package/dist/components/input/variants/input-text.d.ts.map +1 -1
- package/dist/components/input/variants/input-text.js +1 -1
- package/dist/components/input/variants/input-text.js.map +1 -1
- package/dist/components/input/variants/input-textarea.js.map +1 -1
- package/dist/components/loading/loading.js.map +1 -1
- package/dist/components/mansory/mansory.js.map +1 -1
- package/dist/components/menu/menu.js.map +1 -1
- package/dist/components/menu/menu.types.d.ts +2 -3
- package/dist/components/menu/menu.types.d.ts.map +1 -1
- package/dist/components/modal/modal.js.map +1 -1
- package/dist/components/modal/modalContext.js.map +1 -1
- package/dist/components/pagination/pagination.js.map +1 -1
- package/dist/components/progress-bar/progress-bar.js.map +1 -1
- package/dist/components/qr-code/qr-code.js.map +1 -1
- package/dist/components/select/select.js.map +1 -1
- package/dist/components/select-zone/select-zone.js.map +1 -1
- package/dist/components/skeleton/skeleton.js.map +1 -1
- package/dist/components/slider/slider.js.map +1 -1
- package/dist/components/splitter/splitter.js.map +1 -1
- package/dist/components/steps/steps.js.map +1 -1
- package/dist/components/swap/swap.js.map +1 -1
- package/dist/components/switch/switch.js.map +1 -1
- package/dist/components/tab/tab.js.map +1 -1
- package/dist/components/table/table.js.map +1 -1
- package/dist/components/timeline/timeline.js.map +1 -1
- package/dist/components/toast/icons/ErrorIcon.js.map +1 -1
- package/dist/components/toast/icons/IconCircle.js.map +1 -1
- package/dist/components/toast/icons/InfoIcon.js.map +1 -1
- package/dist/components/toast/icons/LoaderIcon.js.map +1 -1
- package/dist/components/toast/icons/SuccessIcon.js.map +1 -1
- package/dist/components/toast/icons/WarningIcon.js.map +1 -1
- package/dist/components/toast/toast.js.map +1 -1
- package/dist/components/toast/toast.store.js.map +1 -1
- package/dist/components/tooltip/tooltip.js.map +1 -1
- package/dist/components/tour/tour.js.map +1 -1
- package/dist/components/upload/upload.js.map +1 -1
- package/dist/components/z-index/z-index.context.js.map +1 -1
- package/dist/components/z-index/z-index.js.map +1 -1
- package/dist/components/z-index/z-index.store.js.map +1 -1
- package/dist/components/z-index/z-index.types.js.map +1 -1
- package/dist/package.json +1 -1
- package/dist/skill/avatar.skill.md.txt +255 -255
- package/dist/skill/badge.skill.md.txt +223 -223
- package/dist/skill/breadcrumb.skill.md.txt +177 -177
- package/dist/skill/button.skill.md.txt +198 -198
- package/dist/skill/carousel.skill.md.txt +406 -406
- package/dist/skill/chat-bubble.skill.md.txt +342 -342
- package/dist/skill/checkbox.skill.md.txt +326 -326
- package/dist/skill/code-preview.skill.md.txt +240 -240
- package/dist/skill/collapse.skill.md.txt +329 -329
- package/dist/skill/context-menu.skill.md.txt +233 -233
- package/dist/skill/diff.skill.md.txt +244 -244
- package/dist/skill/divider.skill.md.txt +151 -151
- package/dist/skill/doc.skill.md.txt +191 -191
- package/dist/skill/drawer.skill.md.txt +157 -157
- package/dist/skill/dropdown.skill.md.txt +198 -198
- package/dist/skill/float-button.skill.md.txt +315 -315
- package/dist/skill/hover-3d-image.skill.md.txt +120 -120
- package/dist/skill/iframe.skill.md.txt +114 -114
- package/dist/skill/image-preview.skill.md.txt +162 -162
- package/dist/skill/indicator.skill.md.txt +60 -60
- package/dist/skill/input.skill.md.txt +489 -489
- package/dist/skill/loading.skill.md.txt +127 -127
- package/dist/skill/menu.skill.md.txt +476 -476
- package/dist/skill/modal.skill.md.txt +359 -359
- package/dist/skill/pagination.skill.md.txt +405 -405
- package/dist/skill/progress-bar.skill.md.txt +207 -207
- package/dist/skill/qr-code.skill.md.txt +136 -136
- package/dist/skill/rating.skill.md.txt +167 -167
- package/dist/skill/select-zone.skill.md.txt +93 -93
- package/dist/skill/select.skill.md.txt +663 -663
- package/dist/skill/skeleton.skill.md.txt +192 -192
- package/dist/skill/slider.skill.md.txt +404 -404
- package/dist/skill/splitter.skill.md.txt +411 -411
- package/dist/skill/steps.skill.md.txt +264 -264
- package/dist/skill/swap.skill.md.txt +139 -139
- package/dist/skill/switch.skill.md.txt +191 -191
- package/dist/skill/tab.skill.md.txt +484 -484
- package/dist/skill/table.example.header.md.txt +666 -666
- package/dist/skill/table.skill.md.txt +1407 -1407
- package/dist/skill/text-rotate.skill.md.txt +186 -186
- package/dist/skill/timeline.skill.md.txt +247 -247
- package/dist/skill/toast.skill.md.txt +531 -531
- package/dist/skill/tooltip.skill.md.txt +222 -222
- package/dist/skill/tour.skill.md.txt +156 -156
- package/dist/skill/upload.skill.md.txt +358 -358
- package/dist/utils/cn.js.map +1 -1
- package/dist/utils/element-tracker.js.map +1 -1
- package/dist/utils/helper.js.map +1 -1
- package/dist/utils/hoc.js.map +1 -1
- package/package.json +132 -133
|
@@ -1,411 +1,411 @@
|
|
|
1
|
-
## COMPONENT IDENTITY
|
|
2
|
-
- **Import**: `import { Splitter } from 'solid-tom-ui';`
|
|
3
|
-
- **Export**: `Splitter` (main), `Splitter.Panel` (sub-component)
|
|
4
|
-
- **Framework**: SolidJS
|
|
5
|
-
- **Purpose**: Resizable panel layout — renders two or more panels separated by draggable dividers; supports collapse and programmatic control
|
|
6
|
-
|
|
7
|
-
---
|
|
8
|
-
|
|
9
|
-
## Basic Usage
|
|
10
|
-
|
|
11
|
-
The `Splitter` component uses a **compound component** pattern. Always wrap panels with `<Splitter.Panel>`.
|
|
12
|
-
|
|
13
|
-
```tsx
|
|
14
|
-
// Horizontal (default) — two panels at 40% / 60%
|
|
15
|
-
<div class="h-40">
|
|
16
|
-
<Splitter>
|
|
17
|
-
<Splitter.Panel defaultSize={40}>
|
|
18
|
-
<div>Left</div>
|
|
19
|
-
</Splitter.Panel>
|
|
20
|
-
<Splitter.Panel defaultSize={60}>
|
|
21
|
-
<div>Right</div>
|
|
22
|
-
</Splitter.Panel>
|
|
23
|
-
</Splitter>
|
|
24
|
-
</div>
|
|
25
|
-
```
|
|
26
|
-
|
|
27
|
-
> **Important:** The `Splitter` container must have an explicit height (or be inside a flex/grid parent with a defined height). Without it, panels will not render correctly.
|
|
28
|
-
|
|
29
|
-
---
|
|
30
|
-
|
|
31
|
-
## `Splitter` Props
|
|
32
|
-
|
|
33
|
-
| Prop | Type | Default | Description |
|
|
34
|
-
|---|---|---|---|
|
|
35
|
-
| `vertical` | `boolean` | `false` | Stack panels top-to-bottom instead of left-to-right. Shorthand for `orientation="vertical"`. |
|
|
36
|
-
| `orientation` | `'horizontal' \| 'vertical'` | `'horizontal'` | Explicit orientation. Prefer `vertical` shorthand. |
|
|
37
|
-
| `lazy` | `boolean` | `false` | Defer size updates until mouse release. A ghost line shows the target position during drag. |
|
|
38
|
-
| `size` | `number[]` | — | **Controlled mode.** Array of sizes (percent) for each panel. Must provide `onResize` to update. |
|
|
39
|
-
| `color` | `BaseColorProps` | `'primary'` | Color token applied to dragger highlights. |
|
|
40
|
-
| `children` | `JSX.Element` | **required** | Must contain one or more `<Splitter.Panel>`. |
|
|
41
|
-
| `class` | `Partial<Record<'root'\|'panel'\|'dragger'\|'draggerBar'\|'collapseBtn', string>>` | — | Per-slot class overrides. |
|
|
42
|
-
| `onResizeStart` | `(e: ResizeEvent) => void` | — | Fired when drag starts. |
|
|
43
|
-
| `onResize` | `(e: ResizeEvent) => void` | — | Fired on every size change during drag (or on release in lazy mode). |
|
|
44
|
-
| `onResizeEnd` | `(e: ResizeEvent) => void` | — | Fired when drag ends. |
|
|
45
|
-
| `onDraggerDoubleClick` | `(index: number, sizes: number[]) => void` | — | Fired on double-click of a dragger. Use to reset sizes. |
|
|
46
|
-
| `draggerIcon` | `JSX.Element \| (() => JSX.Element)` | — | Custom icon rendered inside the dragger bar. |
|
|
47
|
-
|
|
48
|
-
> **Collapse props are split into two mutually exclusive modes** — see the section below.
|
|
49
|
-
|
|
50
|
-
### `ResizeEvent`
|
|
51
|
-
|
|
52
|
-
```ts
|
|
53
|
-
{ sizes: number[]; index: number }
|
|
54
|
-
// sizes — current percent sizes of all panels
|
|
55
|
-
// index — dragger index that triggered the event
|
|
56
|
-
```
|
|
57
|
-
|
|
58
|
-
### `CollapseEvent`
|
|
59
|
-
|
|
60
|
-
```ts
|
|
61
|
-
{ sizes: number[]; collapsed: boolean[]; index: number }
|
|
62
|
-
// collapsed — boolean array, true means that panel is currently collapsed
|
|
63
|
-
// index — panel index that was toggled
|
|
64
|
-
```
|
|
65
|
-
|
|
66
|
-
---
|
|
67
|
-
|
|
68
|
-
## Collapse Modes (Discriminated Union)
|
|
69
|
-
|
|
70
|
-
`SplitterProps` is a **discriminated union** — you must choose one of two collapse modes. Mixing props from both modes is a TypeScript error.
|
|
71
|
-
|
|
72
|
-
### Uncontrolled Collapse (default)
|
|
73
|
-
|
|
74
|
-
Splitter manages collapse state internally. Icon buttons and double-click trigger collapse.
|
|
75
|
-
|
|
76
|
-
| Prop | Type | Default | Description |
|
|
77
|
-
|---|---|---|---|
|
|
78
|
-
| `showCollapsibleIcon` | `boolean \| 'auto'` | `'auto'` | `'auto'` = icons visible on hover only. `true` = always visible. `false` = icons hidden (double-click only). |
|
|
79
|
-
| `collapsibleIcon` | `{ collapse?, expand? }` | — | Custom collapse/expand icons. Each accepts `JSX.Element` or a function returning one. |
|
|
80
|
-
| `onCollapse` | `(e: CollapseEvent) => void` | — | Fired when a panel is collapsed or expanded. |
|
|
81
|
-
|
|
82
|
-
```tsx
|
|
83
|
-
// Uncontrolled: component owns collapse state
|
|
84
|
-
<Splitter showCollapsibleIcon="auto" onCollapse={e => console.log(e.collapsed)}>
|
|
85
|
-
<Splitter.Panel defaultSize={30} collapsible>Sidebar</Splitter.Panel>
|
|
86
|
-
<Splitter.Panel defaultSize={70}>Content</Splitter.Panel>
|
|
87
|
-
</Splitter>
|
|
88
|
-
```
|
|
89
|
-
|
|
90
|
-
### Controlled Collapse
|
|
91
|
-
|
|
92
|
-
Caller owns the collapse state. Icon buttons are **disabled entirely** — only the external signal controls which panels are hidden.
|
|
93
|
-
|
|
94
|
-
| Prop | Type | Description |
|
|
95
|
-
|---|---|---|
|
|
96
|
-
| `collapsed` | `boolean[]` | Array of booleans matching panel count. `true` = panel hidden. |
|
|
97
|
-
|
|
98
|
-
> When using `collapsed`, these props are **forbidden** (TypeScript error): `showCollapsibleIcon`, `collapsibleIcon`, `onCollapse`.
|
|
99
|
-
|
|
100
|
-
```tsx
|
|
101
|
-
const [collapsed, setCollapsed] = createSignal([false, false]);
|
|
102
|
-
|
|
103
|
-
<button onClick={() => setCollapsed(p => [!p[0], p[1]])}>Toggle Sidebar</button>
|
|
104
|
-
|
|
105
|
-
<Splitter collapsed={collapsed()}>
|
|
106
|
-
<Splitter.Panel defaultSize={30}>Sidebar</Splitter.Panel>
|
|
107
|
-
<Splitter.Panel defaultSize={70}>Content</Splitter.Panel>
|
|
108
|
-
</Splitter>
|
|
109
|
-
```
|
|
110
|
-
|
|
111
|
-
---
|
|
112
|
-
|
|
113
|
-
## `Splitter.Panel` Props
|
|
114
|
-
|
|
115
|
-
| Prop | Type | Default | Description |
|
|
116
|
-
|---|---|---|---|
|
|
117
|
-
| `defaultSize` | `number` | auto-distributed | Uncontrolled initial size (percent). |
|
|
118
|
-
| `size` | `number` | — | Controlled size per-panel (rarely used; prefer `Splitter size=[]`). |
|
|
119
|
-
| `min` | `number` | `0` | Minimum size (percent). Panel cannot be dragged below this. |
|
|
120
|
-
| `max` | `number` | `100` | Maximum size (percent). Panel cannot be dragged above this. |
|
|
121
|
-
| `resizable` | `boolean` | `true` | Set to `false` to make this panel's adjacent dragger inert. |
|
|
122
|
-
| `collapsible` | `boolean` | `false` | Allow this panel to be collapsed to zero. Only relevant in uncontrolled mode. |
|
|
123
|
-
| `class` | `string` | — | Extra class on the panel wrapper element. |
|
|
124
|
-
| `children` | `JSX.Element` | — | Panel content. |
|
|
125
|
-
|
|
126
|
-
---
|
|
127
|
-
|
|
128
|
-
## Recipes
|
|
129
|
-
|
|
130
|
-
### Vertical split
|
|
131
|
-
|
|
132
|
-
```tsx
|
|
133
|
-
<div class="h-48">
|
|
134
|
-
<Splitter vertical>
|
|
135
|
-
<Splitter.Panel defaultSize={40}>
|
|
136
|
-
<div>Top</div>
|
|
137
|
-
</Splitter.Panel>
|
|
138
|
-
<Splitter.Panel defaultSize={60}>
|
|
139
|
-
<div>Bottom</div>
|
|
140
|
-
</Splitter.Panel>
|
|
141
|
-
</Splitter>
|
|
142
|
-
</div>
|
|
143
|
-
```
|
|
144
|
-
|
|
145
|
-
### Three panels with min/max constraints
|
|
146
|
-
|
|
147
|
-
```tsx
|
|
148
|
-
<Splitter>
|
|
149
|
-
<Splitter.Panel defaultSize={25} min={10} max={50}>Left</Splitter.Panel>
|
|
150
|
-
<Splitter.Panel defaultSize={50} min={20}>Center</Splitter.Panel>
|
|
151
|
-
<Splitter.Panel defaultSize={25} min={10} max={50}>Right</Splitter.Panel>
|
|
152
|
-
</Splitter>
|
|
153
|
-
```
|
|
154
|
-
|
|
155
|
-
### Lazy mode (ghost line during drag)
|
|
156
|
-
|
|
157
|
-
```tsx
|
|
158
|
-
<Splitter lazy>
|
|
159
|
-
<Splitter.Panel defaultSize={50}>Left</Splitter.Panel>
|
|
160
|
-
<Splitter.Panel defaultSize={50}>Right</Splitter.Panel>
|
|
161
|
-
</Splitter>
|
|
162
|
-
```
|
|
163
|
-
|
|
164
|
-
### Collapsible sidebar (one side)
|
|
165
|
-
|
|
166
|
-
```tsx
|
|
167
|
-
// Left panel can be collapsed. Collapse icon appears on dragger hover (default 'auto').
|
|
168
|
-
<Splitter>
|
|
169
|
-
<Splitter.Panel defaultSize={30} min={15} collapsible>
|
|
170
|
-
Sidebar
|
|
171
|
-
</Splitter.Panel>
|
|
172
|
-
<Splitter.Panel defaultSize={70}>
|
|
173
|
-
Main Content
|
|
174
|
-
</Splitter.Panel>
|
|
175
|
-
</Splitter>
|
|
176
|
-
```
|
|
177
|
-
|
|
178
|
-
### Collapsible both sides — icon always visible
|
|
179
|
-
|
|
180
|
-
```tsx
|
|
181
|
-
<Splitter showCollapsibleIcon={true}>
|
|
182
|
-
<Splitter.Panel defaultSize={35} min={10} collapsible>Sidebar</Splitter.Panel>
|
|
183
|
-
<Splitter.Panel defaultSize={65} min={10} collapsible>Editor</Splitter.Panel>
|
|
184
|
-
</Splitter>
|
|
185
|
-
```
|
|
186
|
-
|
|
187
|
-
### Collapsible — icon hidden (double-click dragger only)
|
|
188
|
-
|
|
189
|
-
```tsx
|
|
190
|
-
<Splitter showCollapsibleIcon={false}>
|
|
191
|
-
<Splitter.Panel defaultSize={40} min={10} collapsible>Panel A</Splitter.Panel>
|
|
192
|
-
<Splitter.Panel defaultSize={60} min={10} collapsible>Panel B</Splitter.Panel>
|
|
193
|
-
</Splitter>
|
|
194
|
-
```
|
|
195
|
-
|
|
196
|
-
### Non-resizable panel
|
|
197
|
-
|
|
198
|
-
```tsx
|
|
199
|
-
// Right panel is fixed; its adjacent dragger is disabled
|
|
200
|
-
<Splitter>
|
|
201
|
-
<Splitter.Panel defaultSize={70}>Main</Splitter.Panel>
|
|
202
|
-
<Splitter.Panel defaultSize={30} resizable={false}>Fixed sidebar</Splitter.Panel>
|
|
203
|
-
</Splitter>
|
|
204
|
-
```
|
|
205
|
-
|
|
206
|
-
### Controlled size
|
|
207
|
-
|
|
208
|
-
```tsx
|
|
209
|
-
const [sizes, setSizes] = createSignal([40, 60]);
|
|
210
|
-
|
|
211
|
-
<Splitter size={sizes()} onResize={e => setSizes(e.sizes)}>
|
|
212
|
-
<Splitter.Panel>
|
|
213
|
-
<div>Left {sizes()[0].toFixed(0)}%</div>
|
|
214
|
-
</Splitter.Panel>
|
|
215
|
-
<Splitter.Panel>
|
|
216
|
-
<div>Right {sizes()[1].toFixed(0)}%</div>
|
|
217
|
-
</Splitter.Panel>
|
|
218
|
-
</Splitter>
|
|
219
|
-
```
|
|
220
|
-
|
|
221
|
-
### Double-click dragger to reset sizes
|
|
222
|
-
|
|
223
|
-
```tsx
|
|
224
|
-
const [sizes, setSizes] = createSignal([50, 50]);
|
|
225
|
-
|
|
226
|
-
<Splitter
|
|
227
|
-
size={sizes()}
|
|
228
|
-
onResize={e => setSizes(e.sizes)}
|
|
229
|
-
onDraggerDoubleClick={() => setSizes([50, 50])}
|
|
230
|
-
>
|
|
231
|
-
<Splitter.Panel><div>{sizes()[0].toFixed(0)}%</div></Splitter.Panel>
|
|
232
|
-
<Splitter.Panel><div>{sizes()[1].toFixed(0)}%</div></Splitter.Panel>
|
|
233
|
-
</Splitter>
|
|
234
|
-
```
|
|
235
|
-
|
|
236
|
-
### Controlled collapse — toggle panels from outside
|
|
237
|
-
|
|
238
|
-
```tsx
|
|
239
|
-
const [collapsed, setCollapsed] = createSignal([false, false, false]);
|
|
240
|
-
|
|
241
|
-
const toggle = (idx: number) =>
|
|
242
|
-
setCollapsed(prev => prev.map((v, i) => (i === idx ? !v : v)));
|
|
243
|
-
|
|
244
|
-
// Toolbar buttons control visibility — no icons shown on dragger
|
|
245
|
-
<div class="flex gap-2">
|
|
246
|
-
<button class="btn btn-xs" onClick={() => toggle(0)}>Toggle Sidebar</button>
|
|
247
|
-
<button class="btn btn-xs" onClick={() => toggle(2)}>Toggle Inspector</button>
|
|
248
|
-
</div>
|
|
249
|
-
|
|
250
|
-
<Splitter collapsed={collapsed()}>
|
|
251
|
-
<Splitter.Panel defaultSize={25} min={10}>Left Sidebar</Splitter.Panel>
|
|
252
|
-
<Splitter.Panel defaultSize={50} min={20}>Main Editor</Splitter.Panel>
|
|
253
|
-
<Splitter.Panel defaultSize={25} min={10}>Right Inspector</Splitter.Panel>
|
|
254
|
-
</Splitter>
|
|
255
|
-
```
|
|
256
|
-
|
|
257
|
-
### Controlled collapse — vertical layout
|
|
258
|
-
|
|
259
|
-
```tsx
|
|
260
|
-
const [collapsed, setCollapsed] = createSignal([false, false, false]);
|
|
261
|
-
|
|
262
|
-
<Splitter vertical collapsed={collapsed()}>
|
|
263
|
-
<Splitter.Panel defaultSize={20} min={10}>Header</Splitter.Panel>
|
|
264
|
-
<Splitter.Panel defaultSize={60} min={20}>Content</Splitter.Panel>
|
|
265
|
-
<Splitter.Panel defaultSize={20} min={10}>Footer</Splitter.Panel>
|
|
266
|
-
</Splitter>
|
|
267
|
-
```
|
|
268
|
-
|
|
269
|
-
### Nested splitters — IDE layout (controlled collapse)
|
|
270
|
-
|
|
271
|
-
```tsx
|
|
272
|
-
const [outerCollapsed, setOuterCollapsed] = createSignal([false, false, false]);
|
|
273
|
-
const [innerCollapsed, setInnerCollapsed] = createSignal([false, false]);
|
|
274
|
-
|
|
275
|
-
<div class="h-72">
|
|
276
|
-
<Splitter collapsed={outerCollapsed()}>
|
|
277
|
-
{/* Left sidebar */}
|
|
278
|
-
<Splitter.Panel defaultSize={22} min={10}>
|
|
279
|
-
File Tree
|
|
280
|
-
</Splitter.Panel>
|
|
281
|
-
|
|
282
|
-
{/* Center: editor + terminal stacked vertically */}
|
|
283
|
-
<Splitter.Panel defaultSize={56} min={20}>
|
|
284
|
-
<Splitter vertical collapsed={innerCollapsed()}>
|
|
285
|
-
<Splitter.Panel defaultSize={65} min={20}>Editor</Splitter.Panel>
|
|
286
|
-
<Splitter.Panel defaultSize={35} min={10}>Terminal</Splitter.Panel>
|
|
287
|
-
</Splitter>
|
|
288
|
-
</Splitter.Panel>
|
|
289
|
-
|
|
290
|
-
{/* Right inspector */}
|
|
291
|
-
<Splitter.Panel defaultSize={22} min={10}>
|
|
292
|
-
Inspector
|
|
293
|
-
</Splitter.Panel>
|
|
294
|
-
</Splitter>
|
|
295
|
-
</div>
|
|
296
|
-
```
|
|
297
|
-
|
|
298
|
-
### Nested splitters — IDE layout (uncontrolled collapse)
|
|
299
|
-
|
|
300
|
-
```tsx
|
|
301
|
-
<div class="h-72">
|
|
302
|
-
<Splitter showCollapsibleIcon="auto">
|
|
303
|
-
<Splitter.Panel defaultSize={22} min={10} collapsible>
|
|
304
|
-
File Tree
|
|
305
|
-
</Splitter.Panel>
|
|
306
|
-
<Splitter.Panel defaultSize={56} min={20}>
|
|
307
|
-
<Splitter vertical>
|
|
308
|
-
<Splitter.Panel defaultSize={65} min={20}>Editor</Splitter.Panel>
|
|
309
|
-
<Splitter.Panel defaultSize={35} min={10} collapsible>Terminal</Splitter.Panel>
|
|
310
|
-
</Splitter>
|
|
311
|
-
</Splitter.Panel>
|
|
312
|
-
<Splitter.Panel defaultSize={22} min={10} collapsible>
|
|
313
|
-
Inspector
|
|
314
|
-
</Splitter.Panel>
|
|
315
|
-
</Splitter>
|
|
316
|
-
</div>
|
|
317
|
-
```
|
|
318
|
-
|
|
319
|
-
### Three collapsible panels (always-visible icons)
|
|
320
|
-
|
|
321
|
-
```tsx
|
|
322
|
-
<Splitter showCollapsibleIcon={true}>
|
|
323
|
-
<Splitter.Panel defaultSize={25} min={10} collapsible>Left Sidebar</Splitter.Panel>
|
|
324
|
-
<Splitter.Panel defaultSize={50} min={20}>Main Editor</Splitter.Panel>
|
|
325
|
-
<Splitter.Panel defaultSize={25} min={10} collapsible>Right Panel</Splitter.Panel>
|
|
326
|
-
</Splitter>
|
|
327
|
-
```
|
|
328
|
-
|
|
329
|
-
### Vertical collapsible panels
|
|
330
|
-
|
|
331
|
-
```tsx
|
|
332
|
-
<div class="h-64">
|
|
333
|
-
<Splitter vertical showCollapsibleIcon={true}>
|
|
334
|
-
<Splitter.Panel defaultSize={30} min={10} collapsible>Header</Splitter.Panel>
|
|
335
|
-
<Splitter.Panel defaultSize={45} min={20}>Content</Splitter.Panel>
|
|
336
|
-
<Splitter.Panel defaultSize={25} min={10} collapsible>Footer</Splitter.Panel>
|
|
337
|
-
</Splitter>
|
|
338
|
-
</div>
|
|
339
|
-
```
|
|
340
|
-
|
|
341
|
-
---
|
|
342
|
-
|
|
343
|
-
## Collapse Behavior
|
|
344
|
-
|
|
345
|
-
- A panel marked `collapsible` collapses to **zero size**; its neighbor absorbs the freed space.
|
|
346
|
-
- The previously occupied size is saved internally and restored on expand.
|
|
347
|
-
- In **uncontrolled mode**: collapse is triggered via the icon button **or** by double-clicking the adjacent dragger.
|
|
348
|
-
- In **controlled mode**: collapse is driven entirely by the `collapsed` prop — no icon buttons appear.
|
|
349
|
-
- The `showCollapsibleIcon` prop controls icon visibility (uncontrolled only):
|
|
350
|
-
- `'auto'` (default) — icons appear on dragger hover only.
|
|
351
|
-
- `true` — icons always visible.
|
|
352
|
-
- `false` — icons hidden entirely; only double-click collapses.
|
|
353
|
-
- `onCollapse` fires on both collapse and expand (uncontrolled only), providing the full updated `CollapseEvent`.
|
|
354
|
-
|
|
355
|
-
---
|
|
356
|
-
|
|
357
|
-
## Size Calculation Rules
|
|
358
|
-
|
|
359
|
-
- When all panels have `defaultSize`, values are **normalized** to sum to 100%.
|
|
360
|
-
- Example: `defaultSize={40}` + `defaultSize={60}` → 40% + 60% (already sums to 100, no change).
|
|
361
|
-
- Example: `defaultSize={2}` + `defaultSize={3}` → 40% + 60% (normalized).
|
|
362
|
-
- When some panels omit `defaultSize`, the remaining percentage after defined sizes is split equally among them.
|
|
363
|
-
- In **controlled size mode** (`size` prop), always keep `size.length === number of panels`. Mismatches are silently ignored.
|
|
364
|
-
- In **controlled collapse mode** (`collapsed` prop), always keep `collapsed.length === number of panels`.
|
|
365
|
-
|
|
366
|
-
---
|
|
367
|
-
|
|
368
|
-
## Class Override Slots
|
|
369
|
-
|
|
370
|
-
Use the `class` prop (object) to target individual elements:
|
|
371
|
-
|
|
372
|
-
```tsx
|
|
373
|
-
<Splitter
|
|
374
|
-
class={{
|
|
375
|
-
root: 'rounded-xl overflow-hidden',
|
|
376
|
-
panel: 'bg-base-100',
|
|
377
|
-
dragger: 'bg-base-300 hover:bg-primary/30',
|
|
378
|
-
collapseBtn: 'text-primary',
|
|
379
|
-
}}
|
|
380
|
-
>
|
|
381
|
-
...
|
|
382
|
-
</Splitter>
|
|
383
|
-
```
|
|
384
|
-
|
|
385
|
-
Available slots: `root` · `panel` · `dragger` · `draggerBar` · `collapseBtn`.
|
|
386
|
-
|
|
387
|
-
> **CSS encoding**: internal CSS classes use short encoded names (e.g. `sp01`, `sp02`) per project convention.
|
|
388
|
-
|
|
389
|
-
---
|
|
390
|
-
|
|
391
|
-
## Architecture Note
|
|
392
|
-
|
|
393
|
-
`Splitter.Panel` does **not** render anything itself — it only registers its props into a `PanelRegistryContext`. The parent `Splitter` reads the registry with `<For>` and renders the actual panel `<div>` elements and draggers. This means panel children are rendered inside `Splitter`, not at the `Panel` call site.
|
|
394
|
-
|
|
395
|
-
---
|
|
396
|
-
|
|
397
|
-
## Common Mistakes
|
|
398
|
-
|
|
399
|
-
| Mistake | Fix |
|
|
400
|
-
|---|---|
|
|
401
|
-
| Container has no height | Add explicit `h-*` or ensure parent has a defined height |
|
|
402
|
-
| Controlled `size` array length doesn't match panel count | Keep `size.length === panels.length` |
|
|
403
|
-
| Controlled `collapsed` array length doesn't match panel count | Keep `collapsed.length === panels.length` |
|
|
404
|
-
| Using `showCollapsibleIcon` with `collapsed` prop | TypeScript error — choose one mode; controlled collapse disables icon buttons entirely |
|
|
405
|
-
| Collapsible works but icon never shows | Default `showCollapsibleIcon` is `'auto'` — hover the dragger to reveal |
|
|
406
|
-
| Panel snaps back after drag | Controlled size mode: wire `onResize` to update the signal |
|
|
407
|
-
| `min` seems to prevent collapse | `collapsible` overrides `min` — collapsed panels go to 0 regardless of `min` |
|
|
408
|
-
| Nested Splitter has wrong height | Inner `Splitter.Panel` needs `h-full` or the nested Splitter won't fill its panel |
|
|
409
|
-
| `onCollapse` not firing | Only available in uncontrolled mode — not available when `collapsed` prop is used |
|
|
410
|
-
|
|
411
|
-
> **Unique IDs**: if this component needs to generate HTML `id` attributes, always use `createUniqueId()` from `solid-js` — never `Math.random()` or `Date.now()`.
|
|
1
|
+
## COMPONENT IDENTITY
|
|
2
|
+
- **Import**: `import { Splitter } from 'solid-tom-ui';`
|
|
3
|
+
- **Export**: `Splitter` (main), `Splitter.Panel` (sub-component)
|
|
4
|
+
- **Framework**: SolidJS
|
|
5
|
+
- **Purpose**: Resizable panel layout — renders two or more panels separated by draggable dividers; supports collapse and programmatic control
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Basic Usage
|
|
10
|
+
|
|
11
|
+
The `Splitter` component uses a **compound component** pattern. Always wrap panels with `<Splitter.Panel>`.
|
|
12
|
+
|
|
13
|
+
```tsx
|
|
14
|
+
// Horizontal (default) — two panels at 40% / 60%
|
|
15
|
+
<div class="h-40">
|
|
16
|
+
<Splitter>
|
|
17
|
+
<Splitter.Panel defaultSize={40}>
|
|
18
|
+
<div>Left</div>
|
|
19
|
+
</Splitter.Panel>
|
|
20
|
+
<Splitter.Panel defaultSize={60}>
|
|
21
|
+
<div>Right</div>
|
|
22
|
+
</Splitter.Panel>
|
|
23
|
+
</Splitter>
|
|
24
|
+
</div>
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
> **Important:** The `Splitter` container must have an explicit height (or be inside a flex/grid parent with a defined height). Without it, panels will not render correctly.
|
|
28
|
+
|
|
29
|
+
---
|
|
30
|
+
|
|
31
|
+
## `Splitter` Props
|
|
32
|
+
|
|
33
|
+
| Prop | Type | Default | Description |
|
|
34
|
+
|---|---|---|---|
|
|
35
|
+
| `vertical` | `boolean` | `false` | Stack panels top-to-bottom instead of left-to-right. Shorthand for `orientation="vertical"`. |
|
|
36
|
+
| `orientation` | `'horizontal' \| 'vertical'` | `'horizontal'` | Explicit orientation. Prefer `vertical` shorthand. |
|
|
37
|
+
| `lazy` | `boolean` | `false` | Defer size updates until mouse release. A ghost line shows the target position during drag. |
|
|
38
|
+
| `size` | `number[]` | — | **Controlled mode.** Array of sizes (percent) for each panel. Must provide `onResize` to update. |
|
|
39
|
+
| `color` | `BaseColorProps` | `'primary'` | Color token applied to dragger highlights. |
|
|
40
|
+
| `children` | `JSX.Element` | **required** | Must contain one or more `<Splitter.Panel>`. |
|
|
41
|
+
| `class` | `Partial<Record<'root'\|'panel'\|'dragger'\|'draggerBar'\|'collapseBtn', string>>` | — | Per-slot class overrides. |
|
|
42
|
+
| `onResizeStart` | `(e: ResizeEvent) => void` | — | Fired when drag starts. |
|
|
43
|
+
| `onResize` | `(e: ResizeEvent) => void` | — | Fired on every size change during drag (or on release in lazy mode). |
|
|
44
|
+
| `onResizeEnd` | `(e: ResizeEvent) => void` | — | Fired when drag ends. |
|
|
45
|
+
| `onDraggerDoubleClick` | `(index: number, sizes: number[]) => void` | — | Fired on double-click of a dragger. Use to reset sizes. |
|
|
46
|
+
| `draggerIcon` | `JSX.Element \| (() => JSX.Element)` | — | Custom icon rendered inside the dragger bar. |
|
|
47
|
+
|
|
48
|
+
> **Collapse props are split into two mutually exclusive modes** — see the section below.
|
|
49
|
+
|
|
50
|
+
### `ResizeEvent`
|
|
51
|
+
|
|
52
|
+
```ts
|
|
53
|
+
{ sizes: number[]; index: number }
|
|
54
|
+
// sizes — current percent sizes of all panels
|
|
55
|
+
// index — dragger index that triggered the event
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
### `CollapseEvent`
|
|
59
|
+
|
|
60
|
+
```ts
|
|
61
|
+
{ sizes: number[]; collapsed: boolean[]; index: number }
|
|
62
|
+
// collapsed — boolean array, true means that panel is currently collapsed
|
|
63
|
+
// index — panel index that was toggled
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
---
|
|
67
|
+
|
|
68
|
+
## Collapse Modes (Discriminated Union)
|
|
69
|
+
|
|
70
|
+
`SplitterProps` is a **discriminated union** — you must choose one of two collapse modes. Mixing props from both modes is a TypeScript error.
|
|
71
|
+
|
|
72
|
+
### Uncontrolled Collapse (default)
|
|
73
|
+
|
|
74
|
+
Splitter manages collapse state internally. Icon buttons and double-click trigger collapse.
|
|
75
|
+
|
|
76
|
+
| Prop | Type | Default | Description |
|
|
77
|
+
|---|---|---|---|
|
|
78
|
+
| `showCollapsibleIcon` | `boolean \| 'auto'` | `'auto'` | `'auto'` = icons visible on hover only. `true` = always visible. `false` = icons hidden (double-click only). |
|
|
79
|
+
| `collapsibleIcon` | `{ collapse?, expand? }` | — | Custom collapse/expand icons. Each accepts `JSX.Element` or a function returning one. |
|
|
80
|
+
| `onCollapse` | `(e: CollapseEvent) => void` | — | Fired when a panel is collapsed or expanded. |
|
|
81
|
+
|
|
82
|
+
```tsx
|
|
83
|
+
// Uncontrolled: component owns collapse state
|
|
84
|
+
<Splitter showCollapsibleIcon="auto" onCollapse={e => console.log(e.collapsed)}>
|
|
85
|
+
<Splitter.Panel defaultSize={30} collapsible>Sidebar</Splitter.Panel>
|
|
86
|
+
<Splitter.Panel defaultSize={70}>Content</Splitter.Panel>
|
|
87
|
+
</Splitter>
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
### Controlled Collapse
|
|
91
|
+
|
|
92
|
+
Caller owns the collapse state. Icon buttons are **disabled entirely** — only the external signal controls which panels are hidden.
|
|
93
|
+
|
|
94
|
+
| Prop | Type | Description |
|
|
95
|
+
|---|---|---|
|
|
96
|
+
| `collapsed` | `boolean[]` | Array of booleans matching panel count. `true` = panel hidden. |
|
|
97
|
+
|
|
98
|
+
> When using `collapsed`, these props are **forbidden** (TypeScript error): `showCollapsibleIcon`, `collapsibleIcon`, `onCollapse`.
|
|
99
|
+
|
|
100
|
+
```tsx
|
|
101
|
+
const [collapsed, setCollapsed] = createSignal([false, false]);
|
|
102
|
+
|
|
103
|
+
<button onClick={() => setCollapsed(p => [!p[0], p[1]])}>Toggle Sidebar</button>
|
|
104
|
+
|
|
105
|
+
<Splitter collapsed={collapsed()}>
|
|
106
|
+
<Splitter.Panel defaultSize={30}>Sidebar</Splitter.Panel>
|
|
107
|
+
<Splitter.Panel defaultSize={70}>Content</Splitter.Panel>
|
|
108
|
+
</Splitter>
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
---
|
|
112
|
+
|
|
113
|
+
## `Splitter.Panel` Props
|
|
114
|
+
|
|
115
|
+
| Prop | Type | Default | Description |
|
|
116
|
+
|---|---|---|---|
|
|
117
|
+
| `defaultSize` | `number` | auto-distributed | Uncontrolled initial size (percent). |
|
|
118
|
+
| `size` | `number` | — | Controlled size per-panel (rarely used; prefer `Splitter size=[]`). |
|
|
119
|
+
| `min` | `number` | `0` | Minimum size (percent). Panel cannot be dragged below this. |
|
|
120
|
+
| `max` | `number` | `100` | Maximum size (percent). Panel cannot be dragged above this. |
|
|
121
|
+
| `resizable` | `boolean` | `true` | Set to `false` to make this panel's adjacent dragger inert. |
|
|
122
|
+
| `collapsible` | `boolean` | `false` | Allow this panel to be collapsed to zero. Only relevant in uncontrolled mode. |
|
|
123
|
+
| `class` | `string` | — | Extra class on the panel wrapper element. |
|
|
124
|
+
| `children` | `JSX.Element` | — | Panel content. |
|
|
125
|
+
|
|
126
|
+
---
|
|
127
|
+
|
|
128
|
+
## Recipes
|
|
129
|
+
|
|
130
|
+
### Vertical split
|
|
131
|
+
|
|
132
|
+
```tsx
|
|
133
|
+
<div class="h-48">
|
|
134
|
+
<Splitter vertical>
|
|
135
|
+
<Splitter.Panel defaultSize={40}>
|
|
136
|
+
<div>Top</div>
|
|
137
|
+
</Splitter.Panel>
|
|
138
|
+
<Splitter.Panel defaultSize={60}>
|
|
139
|
+
<div>Bottom</div>
|
|
140
|
+
</Splitter.Panel>
|
|
141
|
+
</Splitter>
|
|
142
|
+
</div>
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
### Three panels with min/max constraints
|
|
146
|
+
|
|
147
|
+
```tsx
|
|
148
|
+
<Splitter>
|
|
149
|
+
<Splitter.Panel defaultSize={25} min={10} max={50}>Left</Splitter.Panel>
|
|
150
|
+
<Splitter.Panel defaultSize={50} min={20}>Center</Splitter.Panel>
|
|
151
|
+
<Splitter.Panel defaultSize={25} min={10} max={50}>Right</Splitter.Panel>
|
|
152
|
+
</Splitter>
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
### Lazy mode (ghost line during drag)
|
|
156
|
+
|
|
157
|
+
```tsx
|
|
158
|
+
<Splitter lazy>
|
|
159
|
+
<Splitter.Panel defaultSize={50}>Left</Splitter.Panel>
|
|
160
|
+
<Splitter.Panel defaultSize={50}>Right</Splitter.Panel>
|
|
161
|
+
</Splitter>
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
### Collapsible sidebar (one side)
|
|
165
|
+
|
|
166
|
+
```tsx
|
|
167
|
+
// Left panel can be collapsed. Collapse icon appears on dragger hover (default 'auto').
|
|
168
|
+
<Splitter>
|
|
169
|
+
<Splitter.Panel defaultSize={30} min={15} collapsible>
|
|
170
|
+
Sidebar
|
|
171
|
+
</Splitter.Panel>
|
|
172
|
+
<Splitter.Panel defaultSize={70}>
|
|
173
|
+
Main Content
|
|
174
|
+
</Splitter.Panel>
|
|
175
|
+
</Splitter>
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
### Collapsible both sides — icon always visible
|
|
179
|
+
|
|
180
|
+
```tsx
|
|
181
|
+
<Splitter showCollapsibleIcon={true}>
|
|
182
|
+
<Splitter.Panel defaultSize={35} min={10} collapsible>Sidebar</Splitter.Panel>
|
|
183
|
+
<Splitter.Panel defaultSize={65} min={10} collapsible>Editor</Splitter.Panel>
|
|
184
|
+
</Splitter>
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
### Collapsible — icon hidden (double-click dragger only)
|
|
188
|
+
|
|
189
|
+
```tsx
|
|
190
|
+
<Splitter showCollapsibleIcon={false}>
|
|
191
|
+
<Splitter.Panel defaultSize={40} min={10} collapsible>Panel A</Splitter.Panel>
|
|
192
|
+
<Splitter.Panel defaultSize={60} min={10} collapsible>Panel B</Splitter.Panel>
|
|
193
|
+
</Splitter>
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
### Non-resizable panel
|
|
197
|
+
|
|
198
|
+
```tsx
|
|
199
|
+
// Right panel is fixed; its adjacent dragger is disabled
|
|
200
|
+
<Splitter>
|
|
201
|
+
<Splitter.Panel defaultSize={70}>Main</Splitter.Panel>
|
|
202
|
+
<Splitter.Panel defaultSize={30} resizable={false}>Fixed sidebar</Splitter.Panel>
|
|
203
|
+
</Splitter>
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
### Controlled size
|
|
207
|
+
|
|
208
|
+
```tsx
|
|
209
|
+
const [sizes, setSizes] = createSignal([40, 60]);
|
|
210
|
+
|
|
211
|
+
<Splitter size={sizes()} onResize={e => setSizes(e.sizes)}>
|
|
212
|
+
<Splitter.Panel>
|
|
213
|
+
<div>Left {sizes()[0].toFixed(0)}%</div>
|
|
214
|
+
</Splitter.Panel>
|
|
215
|
+
<Splitter.Panel>
|
|
216
|
+
<div>Right {sizes()[1].toFixed(0)}%</div>
|
|
217
|
+
</Splitter.Panel>
|
|
218
|
+
</Splitter>
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
### Double-click dragger to reset sizes
|
|
222
|
+
|
|
223
|
+
```tsx
|
|
224
|
+
const [sizes, setSizes] = createSignal([50, 50]);
|
|
225
|
+
|
|
226
|
+
<Splitter
|
|
227
|
+
size={sizes()}
|
|
228
|
+
onResize={e => setSizes(e.sizes)}
|
|
229
|
+
onDraggerDoubleClick={() => setSizes([50, 50])}
|
|
230
|
+
>
|
|
231
|
+
<Splitter.Panel><div>{sizes()[0].toFixed(0)}%</div></Splitter.Panel>
|
|
232
|
+
<Splitter.Panel><div>{sizes()[1].toFixed(0)}%</div></Splitter.Panel>
|
|
233
|
+
</Splitter>
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
### Controlled collapse — toggle panels from outside
|
|
237
|
+
|
|
238
|
+
```tsx
|
|
239
|
+
const [collapsed, setCollapsed] = createSignal([false, false, false]);
|
|
240
|
+
|
|
241
|
+
const toggle = (idx: number) =>
|
|
242
|
+
setCollapsed(prev => prev.map((v, i) => (i === idx ? !v : v)));
|
|
243
|
+
|
|
244
|
+
// Toolbar buttons control visibility — no icons shown on dragger
|
|
245
|
+
<div class="flex gap-2">
|
|
246
|
+
<button class="btn btn-xs" onClick={() => toggle(0)}>Toggle Sidebar</button>
|
|
247
|
+
<button class="btn btn-xs" onClick={() => toggle(2)}>Toggle Inspector</button>
|
|
248
|
+
</div>
|
|
249
|
+
|
|
250
|
+
<Splitter collapsed={collapsed()}>
|
|
251
|
+
<Splitter.Panel defaultSize={25} min={10}>Left Sidebar</Splitter.Panel>
|
|
252
|
+
<Splitter.Panel defaultSize={50} min={20}>Main Editor</Splitter.Panel>
|
|
253
|
+
<Splitter.Panel defaultSize={25} min={10}>Right Inspector</Splitter.Panel>
|
|
254
|
+
</Splitter>
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
### Controlled collapse — vertical layout
|
|
258
|
+
|
|
259
|
+
```tsx
|
|
260
|
+
const [collapsed, setCollapsed] = createSignal([false, false, false]);
|
|
261
|
+
|
|
262
|
+
<Splitter vertical collapsed={collapsed()}>
|
|
263
|
+
<Splitter.Panel defaultSize={20} min={10}>Header</Splitter.Panel>
|
|
264
|
+
<Splitter.Panel defaultSize={60} min={20}>Content</Splitter.Panel>
|
|
265
|
+
<Splitter.Panel defaultSize={20} min={10}>Footer</Splitter.Panel>
|
|
266
|
+
</Splitter>
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
### Nested splitters — IDE layout (controlled collapse)
|
|
270
|
+
|
|
271
|
+
```tsx
|
|
272
|
+
const [outerCollapsed, setOuterCollapsed] = createSignal([false, false, false]);
|
|
273
|
+
const [innerCollapsed, setInnerCollapsed] = createSignal([false, false]);
|
|
274
|
+
|
|
275
|
+
<div class="h-72">
|
|
276
|
+
<Splitter collapsed={outerCollapsed()}>
|
|
277
|
+
{/* Left sidebar */}
|
|
278
|
+
<Splitter.Panel defaultSize={22} min={10}>
|
|
279
|
+
File Tree
|
|
280
|
+
</Splitter.Panel>
|
|
281
|
+
|
|
282
|
+
{/* Center: editor + terminal stacked vertically */}
|
|
283
|
+
<Splitter.Panel defaultSize={56} min={20}>
|
|
284
|
+
<Splitter vertical collapsed={innerCollapsed()}>
|
|
285
|
+
<Splitter.Panel defaultSize={65} min={20}>Editor</Splitter.Panel>
|
|
286
|
+
<Splitter.Panel defaultSize={35} min={10}>Terminal</Splitter.Panel>
|
|
287
|
+
</Splitter>
|
|
288
|
+
</Splitter.Panel>
|
|
289
|
+
|
|
290
|
+
{/* Right inspector */}
|
|
291
|
+
<Splitter.Panel defaultSize={22} min={10}>
|
|
292
|
+
Inspector
|
|
293
|
+
</Splitter.Panel>
|
|
294
|
+
</Splitter>
|
|
295
|
+
</div>
|
|
296
|
+
```
|
|
297
|
+
|
|
298
|
+
### Nested splitters — IDE layout (uncontrolled collapse)
|
|
299
|
+
|
|
300
|
+
```tsx
|
|
301
|
+
<div class="h-72">
|
|
302
|
+
<Splitter showCollapsibleIcon="auto">
|
|
303
|
+
<Splitter.Panel defaultSize={22} min={10} collapsible>
|
|
304
|
+
File Tree
|
|
305
|
+
</Splitter.Panel>
|
|
306
|
+
<Splitter.Panel defaultSize={56} min={20}>
|
|
307
|
+
<Splitter vertical>
|
|
308
|
+
<Splitter.Panel defaultSize={65} min={20}>Editor</Splitter.Panel>
|
|
309
|
+
<Splitter.Panel defaultSize={35} min={10} collapsible>Terminal</Splitter.Panel>
|
|
310
|
+
</Splitter>
|
|
311
|
+
</Splitter.Panel>
|
|
312
|
+
<Splitter.Panel defaultSize={22} min={10} collapsible>
|
|
313
|
+
Inspector
|
|
314
|
+
</Splitter.Panel>
|
|
315
|
+
</Splitter>
|
|
316
|
+
</div>
|
|
317
|
+
```
|
|
318
|
+
|
|
319
|
+
### Three collapsible panels (always-visible icons)
|
|
320
|
+
|
|
321
|
+
```tsx
|
|
322
|
+
<Splitter showCollapsibleIcon={true}>
|
|
323
|
+
<Splitter.Panel defaultSize={25} min={10} collapsible>Left Sidebar</Splitter.Panel>
|
|
324
|
+
<Splitter.Panel defaultSize={50} min={20}>Main Editor</Splitter.Panel>
|
|
325
|
+
<Splitter.Panel defaultSize={25} min={10} collapsible>Right Panel</Splitter.Panel>
|
|
326
|
+
</Splitter>
|
|
327
|
+
```
|
|
328
|
+
|
|
329
|
+
### Vertical collapsible panels
|
|
330
|
+
|
|
331
|
+
```tsx
|
|
332
|
+
<div class="h-64">
|
|
333
|
+
<Splitter vertical showCollapsibleIcon={true}>
|
|
334
|
+
<Splitter.Panel defaultSize={30} min={10} collapsible>Header</Splitter.Panel>
|
|
335
|
+
<Splitter.Panel defaultSize={45} min={20}>Content</Splitter.Panel>
|
|
336
|
+
<Splitter.Panel defaultSize={25} min={10} collapsible>Footer</Splitter.Panel>
|
|
337
|
+
</Splitter>
|
|
338
|
+
</div>
|
|
339
|
+
```
|
|
340
|
+
|
|
341
|
+
---
|
|
342
|
+
|
|
343
|
+
## Collapse Behavior
|
|
344
|
+
|
|
345
|
+
- A panel marked `collapsible` collapses to **zero size**; its neighbor absorbs the freed space.
|
|
346
|
+
- The previously occupied size is saved internally and restored on expand.
|
|
347
|
+
- In **uncontrolled mode**: collapse is triggered via the icon button **or** by double-clicking the adjacent dragger.
|
|
348
|
+
- In **controlled mode**: collapse is driven entirely by the `collapsed` prop — no icon buttons appear.
|
|
349
|
+
- The `showCollapsibleIcon` prop controls icon visibility (uncontrolled only):
|
|
350
|
+
- `'auto'` (default) — icons appear on dragger hover only.
|
|
351
|
+
- `true` — icons always visible.
|
|
352
|
+
- `false` — icons hidden entirely; only double-click collapses.
|
|
353
|
+
- `onCollapse` fires on both collapse and expand (uncontrolled only), providing the full updated `CollapseEvent`.
|
|
354
|
+
|
|
355
|
+
---
|
|
356
|
+
|
|
357
|
+
## Size Calculation Rules
|
|
358
|
+
|
|
359
|
+
- When all panels have `defaultSize`, values are **normalized** to sum to 100%.
|
|
360
|
+
- Example: `defaultSize={40}` + `defaultSize={60}` → 40% + 60% (already sums to 100, no change).
|
|
361
|
+
- Example: `defaultSize={2}` + `defaultSize={3}` → 40% + 60% (normalized).
|
|
362
|
+
- When some panels omit `defaultSize`, the remaining percentage after defined sizes is split equally among them.
|
|
363
|
+
- In **controlled size mode** (`size` prop), always keep `size.length === number of panels`. Mismatches are silently ignored.
|
|
364
|
+
- In **controlled collapse mode** (`collapsed` prop), always keep `collapsed.length === number of panels`.
|
|
365
|
+
|
|
366
|
+
---
|
|
367
|
+
|
|
368
|
+
## Class Override Slots
|
|
369
|
+
|
|
370
|
+
Use the `class` prop (object) to target individual elements:
|
|
371
|
+
|
|
372
|
+
```tsx
|
|
373
|
+
<Splitter
|
|
374
|
+
class={{
|
|
375
|
+
root: 'rounded-xl overflow-hidden',
|
|
376
|
+
panel: 'bg-base-100',
|
|
377
|
+
dragger: 'bg-base-300 hover:bg-primary/30',
|
|
378
|
+
collapseBtn: 'text-primary',
|
|
379
|
+
}}
|
|
380
|
+
>
|
|
381
|
+
...
|
|
382
|
+
</Splitter>
|
|
383
|
+
```
|
|
384
|
+
|
|
385
|
+
Available slots: `root` · `panel` · `dragger` · `draggerBar` · `collapseBtn`.
|
|
386
|
+
|
|
387
|
+
> **CSS encoding**: internal CSS classes use short encoded names (e.g. `sp01`, `sp02`) per project convention.
|
|
388
|
+
|
|
389
|
+
---
|
|
390
|
+
|
|
391
|
+
## Architecture Note
|
|
392
|
+
|
|
393
|
+
`Splitter.Panel` does **not** render anything itself — it only registers its props into a `PanelRegistryContext`. The parent `Splitter` reads the registry with `<For>` and renders the actual panel `<div>` elements and draggers. This means panel children are rendered inside `Splitter`, not at the `Panel` call site.
|
|
394
|
+
|
|
395
|
+
---
|
|
396
|
+
|
|
397
|
+
## Common Mistakes
|
|
398
|
+
|
|
399
|
+
| Mistake | Fix |
|
|
400
|
+
|---|---|
|
|
401
|
+
| Container has no height | Add explicit `h-*` or ensure parent has a defined height |
|
|
402
|
+
| Controlled `size` array length doesn't match panel count | Keep `size.length === panels.length` |
|
|
403
|
+
| Controlled `collapsed` array length doesn't match panel count | Keep `collapsed.length === panels.length` |
|
|
404
|
+
| Using `showCollapsibleIcon` with `collapsed` prop | TypeScript error — choose one mode; controlled collapse disables icon buttons entirely |
|
|
405
|
+
| Collapsible works but icon never shows | Default `showCollapsibleIcon` is `'auto'` — hover the dragger to reveal |
|
|
406
|
+
| Panel snaps back after drag | Controlled size mode: wire `onResize` to update the signal |
|
|
407
|
+
| `min` seems to prevent collapse | `collapsible` overrides `min` — collapsed panels go to 0 regardless of `min` |
|
|
408
|
+
| Nested Splitter has wrong height | Inner `Splitter.Panel` needs `h-full` or the nested Splitter won't fill its panel |
|
|
409
|
+
| `onCollapse` not firing | Only available in uncontrolled mode — not available when `collapsed` prop is used |
|
|
410
|
+
|
|
411
|
+
> **Unique IDs**: if this component needs to generate HTML `id` attributes, always use `createUniqueId()` from `solid-js` — never `Math.random()` or `Date.now()`.
|