react-split-pane 2.0.3 â 3.0.0
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 +342 -144
- package/dist/components/Divider.d.ts +31 -0
- package/dist/components/Divider.d.ts.map +1 -0
- package/dist/components/Pane.d.ts +27 -0
- package/dist/components/Pane.d.ts.map +1 -0
- package/dist/components/SplitPane.d.ts +29 -0
- package/dist/components/SplitPane.d.ts.map +1 -0
- package/dist/hooks/useKeyboardResize.d.ts +34 -0
- package/dist/hooks/useKeyboardResize.d.ts.map +1 -0
- package/dist/hooks/usePaneSize.d.ts +16 -0
- package/dist/hooks/usePaneSize.d.ts.map +1 -0
- package/dist/hooks/useResizer.d.ts +44 -0
- package/dist/hooks/useResizer.d.ts.map +1 -0
- package/dist/index.cjs +2 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -0
- package/dist/keyboard.cjs +2 -0
- package/dist/keyboard.cjs.map +1 -0
- package/dist/keyboard.d.ts +3 -0
- package/dist/keyboard.d.ts.map +1 -0
- package/dist/keyboard.js +2 -0
- package/dist/keyboard.js.map +1 -0
- package/dist/persistence.cjs +2 -0
- package/dist/persistence.cjs.map +1 -0
- package/dist/persistence.d.ts +24 -0
- package/dist/persistence.d.ts.map +1 -0
- package/dist/persistence.js +2 -0
- package/dist/persistence.js.map +1 -0
- package/dist/test/setup.d.ts +2 -0
- package/dist/test/setup.d.ts.map +1 -0
- package/dist/types/index.d.ts +89 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/utils/accessibility.d.ts +17 -0
- package/dist/utils/accessibility.d.ts.map +1 -0
- package/dist/utils/calculations.d.ts +29 -0
- package/dist/utils/calculations.d.ts.map +1 -0
- package/package.json +82 -67
- package/styles.css +106 -0
- package/.editorconfig +0 -6
- package/.storybook/addons.js +0 -2
- package/.storybook/config.js +0 -9
- package/.storybook/preview-head.html +0 -33
- package/.travis.yml +0 -10
- package/index.d.ts +0 -53
- package/index.js +0 -3
- package/lcov.info +0 -700
- package/lib/Pane.js +0 -130
- package/lib/Resizer.js +0 -105
- package/lib/SplitPane.js +0 -512
- package/stories/index.stories.js +0 -40
- package/tsconfig.json +0 -11
package/README.md
CHANGED
|
@@ -1,183 +1,381 @@
|
|
|
1
|
-
# React Split Pane
|
|
1
|
+
# React Split Pane v3
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Modern, accessible, TypeScript-first split pane component for React.
|
|
4
4
|
|
|
5
|
+
[](https://www.npmjs.com/package/react-split-pane)
|
|
6
|
+

|
|
7
|
+
[](https://bundlephobia.com/package/react-split-pane)
|
|
8
|
+
|
|
9
|
+
**[Live Examples](https://tomkp.github.io/react-split-pane)**
|
|
10
|
+
|
|
11
|
+
## ⨠Features
|
|
12
|
+
|
|
13
|
+
- đĒ **Hooks-based** - Built with modern React patterns
|
|
14
|
+
- đ **TypeScript** - Full type safety out of the box
|
|
15
|
+
- âŋ **Accessible** - Keyboard navigation, ARIA attributes, screen reader support
|
|
16
|
+
- đą **Touch-friendly** - Full mobile/tablet support
|
|
17
|
+
- đ¯ **Flexible** - Controlled/uncontrolled modes, nested layouts, 2+ panes
|
|
18
|
+
- đĒļ **Lightweight** - < 5KB gzipped
|
|
19
|
+
- ⥠**Performant** - RAF-throttled resize, optimized renders
|
|
20
|
+
- đ¨ **Customizable** - Full styling control
|
|
21
|
+
|
|
22
|
+
## Installation
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
npm install react-split-pane@next
|
|
26
|
+
|
|
27
|
+
# or
|
|
28
|
+
yarn add react-split-pane@next
|
|
29
|
+
|
|
30
|
+
# or
|
|
31
|
+
pnpm add react-split-pane@next
|
|
5
32
|
```
|
|
6
|
-
|
|
33
|
+
|
|
34
|
+
## Quick Start
|
|
35
|
+
|
|
36
|
+
```tsx
|
|
37
|
+
import { SplitPane, Pane } from 'react-split-pane';
|
|
38
|
+
|
|
39
|
+
function App() {
|
|
40
|
+
return (
|
|
41
|
+
<SplitPane direction="horizontal">
|
|
42
|
+
<Pane minSize="200px" defaultSize="300px">
|
|
43
|
+
<Sidebar />
|
|
44
|
+
</Pane>
|
|
45
|
+
<Pane>
|
|
46
|
+
<MainContent />
|
|
47
|
+
</Pane>
|
|
48
|
+
</SplitPane>
|
|
49
|
+
);
|
|
50
|
+
}
|
|
7
51
|
```
|
|
8
52
|
|
|
9
|
-
|
|
10
|
-
[](https://coveralls.io/r/tomkp/react-split-pane)
|
|
53
|
+
## Basic Usage
|
|
11
54
|
|
|
55
|
+
### Horizontal Split (Side-by-Side)
|
|
12
56
|
|
|
13
|
-
|
|
57
|
+
```tsx
|
|
58
|
+
<SplitPane direction="horizontal">
|
|
59
|
+
<Pane defaultSize="25%">
|
|
60
|
+
<LeftPanel />
|
|
61
|
+
</Pane>
|
|
62
|
+
<Pane>
|
|
63
|
+
<RightPanel />
|
|
64
|
+
</Pane>
|
|
65
|
+
</SplitPane>
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
### Vertical Split (Top-Bottom)
|
|
69
|
+
|
|
70
|
+
```tsx
|
|
71
|
+
<SplitPane direction="vertical">
|
|
72
|
+
<Pane defaultSize="100px">
|
|
73
|
+
<Header />
|
|
74
|
+
</Pane>
|
|
75
|
+
<Pane>
|
|
76
|
+
<Content />
|
|
77
|
+
</Pane>
|
|
78
|
+
</SplitPane>
|
|
79
|
+
```
|
|
14
80
|
|
|
81
|
+
### Controlled Mode
|
|
15
82
|
|
|
16
|
-
```
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
83
|
+
```tsx
|
|
84
|
+
function App() {
|
|
85
|
+
const [sizes, setSizes] = useState([300, 500]);
|
|
86
|
+
|
|
87
|
+
return (
|
|
88
|
+
<SplitPane onResize={setSizes}>
|
|
89
|
+
<Pane size={sizes[0]} minSize="200px">
|
|
90
|
+
<Sidebar />
|
|
91
|
+
</Pane>
|
|
92
|
+
<Pane size={sizes[1]}>
|
|
93
|
+
<Main />
|
|
94
|
+
</Pane>
|
|
95
|
+
</SplitPane>
|
|
96
|
+
);
|
|
97
|
+
}
|
|
21
98
|
```
|
|
22
99
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
100
|
+
### Nested Layouts
|
|
101
|
+
|
|
102
|
+
```tsx
|
|
103
|
+
<SplitPane direction="vertical">
|
|
104
|
+
<Pane defaultSize="60px">
|
|
105
|
+
<Header />
|
|
106
|
+
</Pane>
|
|
107
|
+
|
|
108
|
+
<SplitPane direction="horizontal">
|
|
109
|
+
<Pane defaultSize="250px" minSize="150px">
|
|
110
|
+
<Sidebar />
|
|
111
|
+
</Pane>
|
|
112
|
+
|
|
113
|
+
<SplitPane direction="vertical">
|
|
114
|
+
<Pane>
|
|
115
|
+
<Editor />
|
|
116
|
+
</Pane>
|
|
117
|
+
<Pane defaultSize="200px">
|
|
118
|
+
<Console />
|
|
119
|
+
</Pane>
|
|
30
120
|
</SplitPane>
|
|
121
|
+
</SplitPane>
|
|
122
|
+
</SplitPane>
|
|
31
123
|
```
|
|
32
124
|
|
|
33
|
-
|
|
125
|
+
## Advanced Features
|
|
34
126
|
|
|
35
|
-
|
|
36
|
-
The first pane keeps then its size while the second pane is resized by browser window.
|
|
37
|
-
By default it is the left pane for 'vertical' SplitPane and the top pane for 'horizontal' SplitPane.
|
|
38
|
-
If you want to keep size of the second pane and let the first pane to shrink or grow by browser window dimensions,
|
|
39
|
-
set SplitPane prop `primary` to `second`. In case of 'horizontal' SplitPane the height of bottom pane remains the same.
|
|
127
|
+
### Persistence
|
|
40
128
|
|
|
41
|
-
|
|
129
|
+
The `usePersistence` hook saves and restores pane sizes to localStorage (or sessionStorage):
|
|
42
130
|
|
|
43
|
-
|
|
131
|
+
```tsx
|
|
132
|
+
import { usePersistence } from 'react-split-pane/persistence';
|
|
44
133
|
|
|
45
|
-
|
|
134
|
+
function App() {
|
|
135
|
+
const [sizes, setSizes] = usePersistence({ key: 'my-layout' });
|
|
46
136
|
|
|
47
|
-
|
|
48
|
-
<SplitPane
|
|
49
|
-
|
|
50
|
-
<
|
|
137
|
+
return (
|
|
138
|
+
<SplitPane onResize={setSizes}>
|
|
139
|
+
<Pane size={sizes[0] || 300}>
|
|
140
|
+
<Sidebar />
|
|
141
|
+
</Pane>
|
|
142
|
+
<Pane size={sizes[1]}>
|
|
143
|
+
<Main />
|
|
144
|
+
</Pane>
|
|
51
145
|
</SplitPane>
|
|
146
|
+
);
|
|
147
|
+
}
|
|
52
148
|
```
|
|
53
149
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
150
|
+
#### usePersistence Options
|
|
151
|
+
|
|
152
|
+
| Option | Type | Default | Description |
|
|
153
|
+
|--------|------|---------|-------------|
|
|
154
|
+
| `key` | `string` | Required | Storage key for persisting sizes |
|
|
155
|
+
| `storage` | `Storage` | `localStorage` | Storage backend (localStorage or sessionStorage) |
|
|
156
|
+
| `debounce` | `number` | `300` | Debounce delay in ms before saving |
|
|
157
|
+
|
|
158
|
+
```tsx
|
|
159
|
+
// Use sessionStorage instead of localStorage
|
|
160
|
+
const [sizes, setSizes] = usePersistence({
|
|
161
|
+
key: 'my-layout',
|
|
162
|
+
storage: sessionStorage,
|
|
163
|
+
debounce: 500,
|
|
164
|
+
});
|
|
165
|
+
```
|
|
62
166
|
|
|
63
|
-
###
|
|
64
|
-
You can use the step prop to only allow resizing in fixed increments.
|
|
167
|
+
### Snap Points
|
|
65
168
|
|
|
66
|
-
|
|
169
|
+
```tsx
|
|
170
|
+
<SplitPane
|
|
171
|
+
snapPoints={[200, 400, 600]}
|
|
172
|
+
snapTolerance={20}
|
|
173
|
+
>
|
|
174
|
+
{/* panes */}
|
|
175
|
+
</SplitPane>
|
|
176
|
+
```
|
|
67
177
|
|
|
68
|
-
|
|
69
|
-
defaultSize and a persistence layer, you can ensure that your splitter choices
|
|
70
|
-
survive a refresh of your app.
|
|
178
|
+
### Custom Divider
|
|
71
179
|
|
|
72
|
-
|
|
73
|
-
|
|
180
|
+
```tsx
|
|
181
|
+
function CustomDivider(props) {
|
|
182
|
+
return (
|
|
183
|
+
<div {...props} style={{ ...props.style, background: 'blue' }}>
|
|
184
|
+
<GripIcon />
|
|
185
|
+
</div>
|
|
186
|
+
);
|
|
187
|
+
}
|
|
74
188
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
onChange={ size => localStorage.setItem('splitPos', size) }>
|
|
79
|
-
<div></div>
|
|
80
|
-
<div></div>
|
|
81
|
-
</SplitPane>
|
|
189
|
+
<SplitPane divider={CustomDivider}>
|
|
190
|
+
{/* panes */}
|
|
191
|
+
</SplitPane>
|
|
82
192
|
```
|
|
83
193
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
194
|
+
## Keyboard Navigation
|
|
195
|
+
|
|
196
|
+
The divider is fully keyboard accessible:
|
|
197
|
+
|
|
198
|
+
- **Arrow Keys**: Resize by `step` pixels (default: 10px)
|
|
199
|
+
- **Shift + Arrow**: Resize by larger step (default: 50px)
|
|
200
|
+
- **Home**: Minimize left/top pane
|
|
201
|
+
- **End**: Maximize left/top pane
|
|
202
|
+
- **Tab**: Navigate between dividers
|
|
93
203
|
|
|
94
|
-
|
|
95
|
-
https://github.com/mozilla/localForage although hooking it up will be slightly
|
|
96
|
-
more involved. You are likely to be admired by all for judiciously avoiding
|
|
97
|
-
use of localStorage.
|
|
204
|
+
## API Reference
|
|
98
205
|
|
|
99
|
-
###
|
|
206
|
+
### SplitPane Props
|
|
100
207
|
|
|
101
|
-
|
|
102
|
-
|
|
208
|
+
| Prop | Type | Default | Description |
|
|
209
|
+
|------|------|---------|-------------|
|
|
210
|
+
| `direction` | `'horizontal' \| 'vertical'` | `'horizontal'` | Layout direction |
|
|
211
|
+
| `resizable` | `boolean` | `true` | Whether panes can be resized |
|
|
212
|
+
| `snapPoints` | `number[]` | `[]` | Snap points in pixels |
|
|
213
|
+
| `snapTolerance` | `number` | `10` | Snap tolerance in pixels |
|
|
214
|
+
| `step` | `number` | `10` | Keyboard resize step |
|
|
215
|
+
| `onResizeStart` | `(event) => void` | - | Called when resize starts |
|
|
216
|
+
| `onResize` | `(sizes, event) => void` | - | Called during resize |
|
|
217
|
+
| `onResizeEnd` | `(sizes, event) => void` | - | Called when resize ends |
|
|
218
|
+
| `className` | `string` | - | CSS class name |
|
|
219
|
+
| `style` | `CSSProperties` | - | Inline styles |
|
|
220
|
+
| `divider` | `ComponentType` | - | Custom divider component |
|
|
221
|
+
| `dividerClassName` | `string` | - | Divider class name |
|
|
222
|
+
| `dividerStyle` | `CSSProperties` | - | Divider inline styles |
|
|
103
223
|
|
|
104
|
-
###
|
|
224
|
+
### Pane Props
|
|
105
225
|
|
|
106
|
-
|
|
226
|
+
| Prop | Type | Default | Description |
|
|
227
|
+
|------|------|---------|-------------|
|
|
228
|
+
| `defaultSize` | `string \| number` | `'50%'` | Initial size (uncontrolled) |
|
|
229
|
+
| `size` | `string \| number` | - | Controlled size |
|
|
230
|
+
| `minSize` | `string \| number` | `0` | Minimum size |
|
|
231
|
+
| `maxSize` | `string \| number` | `Infinity` | Maximum size |
|
|
232
|
+
| `className` | `string` | - | CSS class name |
|
|
233
|
+
| `style` | `CSSProperties` | - | Inline styles |
|
|
107
234
|
|
|
108
|
-
|
|
235
|
+
## Styling
|
|
109
236
|
|
|
237
|
+
### Default Stylesheet
|
|
238
|
+
|
|
239
|
+
Import the optional default styles with CSS custom properties:
|
|
240
|
+
|
|
241
|
+
```tsx
|
|
242
|
+
import 'react-split-pane/styles.css';
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
Customize via CSS variables:
|
|
110
246
|
|
|
111
247
|
```css
|
|
248
|
+
.my-split-pane {
|
|
249
|
+
--split-pane-divider-size: 8px;
|
|
250
|
+
--split-pane-divider-color: #e0e0e0;
|
|
251
|
+
--split-pane-divider-color-hover: #b0b0b0;
|
|
252
|
+
--split-pane-focus-color: #2196f3;
|
|
253
|
+
}
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
The default styles include dark mode support via `prefers-color-scheme`.
|
|
257
|
+
|
|
258
|
+
### Basic Styles
|
|
259
|
+
|
|
260
|
+
```css
|
|
261
|
+
.split-pane {
|
|
262
|
+
height: 100vh;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
.split-pane-divider {
|
|
266
|
+
background: #e0e0e0;
|
|
267
|
+
transition: background 0.2s;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
.split-pane-divider:hover {
|
|
271
|
+
background: #b0b0b0;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
.split-pane-divider:focus {
|
|
275
|
+
outline: 2px solid #2196f3;
|
|
276
|
+
outline-offset: -2px;
|
|
277
|
+
}
|
|
278
|
+
```
|
|
279
|
+
|
|
280
|
+
### Expanded Hover Area
|
|
281
|
+
|
|
282
|
+
This classic pattern creates a thin visible divider with a larger grabbable area that reveals on hover:
|
|
283
|
+
|
|
284
|
+
```css
|
|
285
|
+
.split-pane-divider {
|
|
286
|
+
background: #000;
|
|
287
|
+
opacity: 0.2;
|
|
288
|
+
z-index: 1;
|
|
289
|
+
box-sizing: border-box;
|
|
290
|
+
background-clip: padding-box;
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
.split-pane-divider:hover {
|
|
294
|
+
transition: all 0.2s ease;
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
.split-pane-divider.horizontal {
|
|
298
|
+
width: 11px;
|
|
299
|
+
margin: 0 -5px;
|
|
300
|
+
border-left: 5px solid rgba(255, 255, 255, 0);
|
|
301
|
+
border-right: 5px solid rgba(255, 255, 255, 0);
|
|
302
|
+
cursor: col-resize;
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
.split-pane-divider.horizontal:hover {
|
|
306
|
+
border-left: 5px solid rgba(0, 0, 0, 0.5);
|
|
307
|
+
border-right: 5px solid rgba(0, 0, 0, 0.5);
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
.split-pane-divider.vertical {
|
|
311
|
+
height: 11px;
|
|
312
|
+
margin: -5px 0;
|
|
313
|
+
border-top: 5px solid rgba(255, 255, 255, 0);
|
|
314
|
+
border-bottom: 5px solid rgba(255, 255, 255, 0);
|
|
315
|
+
cursor: row-resize;
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
.split-pane-divider.vertical:hover {
|
|
319
|
+
border-top: 5px solid rgba(0, 0, 0, 0.5);
|
|
320
|
+
border-bottom: 5px solid rgba(0, 0, 0, 0.5);
|
|
321
|
+
}
|
|
322
|
+
```
|
|
323
|
+
|
|
324
|
+
### Minimal Divider
|
|
325
|
+
|
|
326
|
+
A subtle single-pixel divider:
|
|
327
|
+
|
|
328
|
+
```css
|
|
329
|
+
.split-pane-divider.horizontal {
|
|
330
|
+
width: 1px;
|
|
331
|
+
margin: 0;
|
|
332
|
+
background: linear-gradient(to right, transparent, #ccc, transparent);
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
.split-pane-divider.vertical {
|
|
336
|
+
height: 1px;
|
|
337
|
+
margin: 0;
|
|
338
|
+
background: linear-gradient(to bottom, transparent, #ccc, transparent);
|
|
339
|
+
}
|
|
340
|
+
```
|
|
341
|
+
|
|
342
|
+
## Migration from v0.1.x
|
|
343
|
+
|
|
344
|
+
See [MIGRATION.md](./MIGRATION.md) for detailed migration guide.
|
|
345
|
+
|
|
346
|
+
**Quick changes:**
|
|
347
|
+
|
|
348
|
+
```tsx
|
|
349
|
+
// v0.1.x
|
|
350
|
+
<SplitPane split="vertical" minSize={50} defaultSize={100}>
|
|
351
|
+
<div>Pane 1</div>
|
|
352
|
+
<div>Pane 2</div>
|
|
353
|
+
</SplitPane>
|
|
354
|
+
|
|
355
|
+
// v3
|
|
356
|
+
<SplitPane direction="horizontal">
|
|
357
|
+
<Pane minSize="50px" defaultSize="100px">
|
|
358
|
+
<div>Pane 1</div>
|
|
359
|
+
</Pane>
|
|
360
|
+
<Pane>
|
|
361
|
+
<div>Pane 2</div>
|
|
362
|
+
</Pane>
|
|
363
|
+
</SplitPane>
|
|
364
|
+
```
|
|
365
|
+
|
|
366
|
+
## Browser Support
|
|
367
|
+
|
|
368
|
+
- Chrome/Edge (latest 2 versions)
|
|
369
|
+
- Firefox (latest 2 versions)
|
|
370
|
+
- Safari (latest 2 versions)
|
|
371
|
+
- Mobile browsers (iOS Safari, Chrome Android)
|
|
372
|
+
|
|
373
|
+
**Note:** IE11 is not supported. Use v0.1.x for IE11 compatibility.
|
|
374
|
+
|
|
375
|
+
## Contributing
|
|
376
|
+
|
|
377
|
+
Contributions are welcome! Please see [CONTRIBUTING.md](./CONTRIBUTING.md).
|
|
378
|
+
|
|
379
|
+
## License
|
|
112
380
|
|
|
113
|
-
|
|
114
|
-
background: #000;
|
|
115
|
-
opacity: .2;
|
|
116
|
-
z-index: 1;
|
|
117
|
-
-moz-box-sizing: border-box;
|
|
118
|
-
-webkit-box-sizing: border-box;
|
|
119
|
-
box-sizing: border-box;
|
|
120
|
-
-moz-background-clip: padding;
|
|
121
|
-
-webkit-background-clip: padding;
|
|
122
|
-
background-clip: padding-box;
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
.Resizer:hover {
|
|
126
|
-
-webkit-transition: all 2s ease;
|
|
127
|
-
transition: all 2s ease;
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
.Resizer.horizontal {
|
|
131
|
-
height: 11px;
|
|
132
|
-
margin: -5px 0;
|
|
133
|
-
border-top: 5px solid rgba(255, 255, 255, 0);
|
|
134
|
-
border-bottom: 5px solid rgba(255, 255, 255, 0);
|
|
135
|
-
cursor: row-resize;
|
|
136
|
-
width: 100%;
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
.Resizer.horizontal:hover {
|
|
140
|
-
border-top: 5px solid rgba(0, 0, 0, 0.5);
|
|
141
|
-
border-bottom: 5px solid rgba(0, 0, 0, 0.5);
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
.Resizer.vertical {
|
|
145
|
-
width: 11px;
|
|
146
|
-
margin: 0 -5px;
|
|
147
|
-
border-left: 5px solid rgba(255, 255, 255, 0);
|
|
148
|
-
border-right: 5px solid rgba(255, 255, 255, 0);
|
|
149
|
-
cursor: col-resize;
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
.Resizer.vertical:hover {
|
|
153
|
-
border-left: 5px solid rgba(0, 0, 0, 0.5);
|
|
154
|
-
border-right: 5px solid rgba(0, 0, 0, 0.5);
|
|
155
|
-
}
|
|
156
|
-
.Resizer.disabled {
|
|
157
|
-
cursor: not-allowed;
|
|
158
|
-
}
|
|
159
|
-
.Resizer.disabled:hover {
|
|
160
|
-
border-color: transparent;
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
```
|
|
164
|
-
### Inline Styles
|
|
165
|
-
|
|
166
|
-
You can also pass inline styles to the components via props. These are:
|
|
167
|
-
|
|
168
|
-
* `paneStyle` - Styling to be applied to both panes
|
|
169
|
-
* `pane1Style` - Styling to be applied to the first pane, with precedence over `paneStyle`
|
|
170
|
-
* `pane2Style` - Styling to be applied to the second pane, with precedence over `paneStyle`
|
|
171
|
-
* `resizerStyle` - Styling to be applied to the resizer bar
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
### Contributing
|
|
175
|
-
|
|
176
|
-
I'm always happy to receive Pull Requests for contributions of any kind.
|
|
177
|
-
|
|
178
|
-
Please include tests and/or update the examples if possible.
|
|
179
|
-
|
|
180
|
-
I've been working on an updated version of this library - if you'd like to get involved in any way please ping me a message.
|
|
181
|
-
|
|
182
|
-
Thanks, Tom
|
|
183
|
-
|
|
381
|
+
MIT Š [tomkp](https://github.com/tomkp)
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { DividerProps } from '../types';
|
|
2
|
+
/**
|
|
3
|
+
* The divider component that separates panes and handles resize interactions.
|
|
4
|
+
*
|
|
5
|
+
* This component is automatically rendered between panes by SplitPane.
|
|
6
|
+
* You can provide a custom divider via the `divider` prop on SplitPane.
|
|
7
|
+
*
|
|
8
|
+
* Features:
|
|
9
|
+
* - Keyboard accessible (arrow keys, Home, End)
|
|
10
|
+
* - Touch-friendly for mobile devices
|
|
11
|
+
* - ARIA attributes for screen readers
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* ```tsx
|
|
15
|
+
* // Custom divider component
|
|
16
|
+
* function CustomDivider(props: DividerProps) {
|
|
17
|
+
* return (
|
|
18
|
+
* <div {...props} className="my-divider">
|
|
19
|
+
* <GripIcon />
|
|
20
|
+
* </div>
|
|
21
|
+
* );
|
|
22
|
+
* }
|
|
23
|
+
*
|
|
24
|
+
* <SplitPane divider={CustomDivider}>
|
|
25
|
+
* <Pane>Left</Pane>
|
|
26
|
+
* <Pane>Right</Pane>
|
|
27
|
+
* </SplitPane>
|
|
28
|
+
* ```
|
|
29
|
+
*/
|
|
30
|
+
export declare function Divider(props: DividerProps): import("react/jsx-runtime").JSX.Element;
|
|
31
|
+
//# sourceMappingURL=Divider.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Divider.d.ts","sourceRoot":"","sources":["../../src/components/Divider.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AAQ7C;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,wBAAgB,OAAO,CAAC,KAAK,EAAE,YAAY,2CAgF1C"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { PaneProps } from '../types';
|
|
2
|
+
/**
|
|
3
|
+
* A pane component that must be used as a direct child of SplitPane.
|
|
4
|
+
*
|
|
5
|
+
* Panes can have size constraints and can be either controlled (with `size`)
|
|
6
|
+
* or uncontrolled (with `defaultSize`).
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
* ```tsx
|
|
10
|
+
* // Uncontrolled with constraints
|
|
11
|
+
* <Pane minSize="100px" maxSize="500px" defaultSize="300px">
|
|
12
|
+
* Content here
|
|
13
|
+
* </Pane>
|
|
14
|
+
*
|
|
15
|
+
* // Controlled
|
|
16
|
+
* <Pane size={sizes[0]} minSize={100}>
|
|
17
|
+
* Content here
|
|
18
|
+
* </Pane>
|
|
19
|
+
*
|
|
20
|
+
* // Percentage-based sizing
|
|
21
|
+
* <Pane defaultSize="25%" minSize="10%">
|
|
22
|
+
* Sidebar
|
|
23
|
+
* </Pane>
|
|
24
|
+
* ```
|
|
25
|
+
*/
|
|
26
|
+
export declare const Pane: import("react").ForwardRefExoticComponent<PaneProps & import("react").RefAttributes<HTMLDivElement>>;
|
|
27
|
+
//# sourceMappingURL=Pane.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Pane.d.ts","sourceRoot":"","sources":["../../src/components/Pane.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AAI1C;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,eAAO,MAAM,IAAI,sGA0ChB,CAAC"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { SplitPaneProps } from '../types';
|
|
2
|
+
/**
|
|
3
|
+
* A flexible split pane component that allows resizable pane layouts.
|
|
4
|
+
*
|
|
5
|
+
* Supports horizontal (side-by-side) and vertical (stacked) layouts with
|
|
6
|
+
* mouse, touch, and keyboard interactions. Fully accessible with ARIA attributes.
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
* ```tsx
|
|
10
|
+
* // Basic horizontal split
|
|
11
|
+
* <SplitPane direction="horizontal">
|
|
12
|
+
* <Pane minSize="200px" defaultSize="300px">
|
|
13
|
+
* <Sidebar />
|
|
14
|
+
* </Pane>
|
|
15
|
+
* <Pane>
|
|
16
|
+
* <MainContent />
|
|
17
|
+
* </Pane>
|
|
18
|
+
* </SplitPane>
|
|
19
|
+
*
|
|
20
|
+
* // Controlled mode with state
|
|
21
|
+
* const [sizes, setSizes] = useState([300, 500]);
|
|
22
|
+
* <SplitPane onResize={setSizes}>
|
|
23
|
+
* <Pane size={sizes[0]}>Left</Pane>
|
|
24
|
+
* <Pane size={sizes[1]}>Right</Pane>
|
|
25
|
+
* </SplitPane>
|
|
26
|
+
* ```
|
|
27
|
+
*/
|
|
28
|
+
export declare function SplitPane(props: SplitPaneProps): import("react/jsx-runtime").JSX.Element;
|
|
29
|
+
//# sourceMappingURL=SplitPane.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SplitPane.d.ts","sourceRoot":"","sources":["../../src/components/SplitPane.tsx"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,cAAc,EAA0B,MAAM,UAAU,CAAC;AAUvE;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,wBAAgB,SAAS,CAAC,KAAK,EAAE,cAAc,2CA+R9C"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type { Direction, ResizeEvent } from '../types';
|
|
2
|
+
/**
|
|
3
|
+
* Options for the useKeyboardResize hook.
|
|
4
|
+
*/
|
|
5
|
+
export interface UseKeyboardResizeOptions {
|
|
6
|
+
direction: Direction;
|
|
7
|
+
sizes: number[];
|
|
8
|
+
minSizes: number[];
|
|
9
|
+
maxSizes: number[];
|
|
10
|
+
step?: number | undefined;
|
|
11
|
+
largeStep?: number | undefined;
|
|
12
|
+
onResize?: ((sizes: number[], event: ResizeEvent) => void) | undefined;
|
|
13
|
+
onResizeEnd?: ((sizes: number[], event: ResizeEvent) => void) | undefined;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Hook that handles keyboard-based resizing of panes.
|
|
17
|
+
*
|
|
18
|
+
* This is a low-level hook used internally by SplitPane. For most use cases,
|
|
19
|
+
* you should use the SplitPane component directly.
|
|
20
|
+
*
|
|
21
|
+
* Supported keys:
|
|
22
|
+
* - Arrow keys: Resize by step (default 10px)
|
|
23
|
+
* - Shift + Arrow: Resize by large step (default 50px)
|
|
24
|
+
* - Home: Minimize the left/top pane to its minimum size
|
|
25
|
+
* - End: Maximize the left/top pane to its maximum size
|
|
26
|
+
* - Escape: Restore pane sizes to what they were when keyboard interaction started
|
|
27
|
+
*
|
|
28
|
+
* @param options - Configuration options for keyboard resize
|
|
29
|
+
* @returns Handler for keyboard events
|
|
30
|
+
*/
|
|
31
|
+
export declare function useKeyboardResize(options: UseKeyboardResizeOptions): {
|
|
32
|
+
handleKeyDown: (dividerIndex: number) => (e: React.KeyboardEvent) => void;
|
|
33
|
+
};
|
|
34
|
+
//# sourceMappingURL=useKeyboardResize.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useKeyboardResize.d.ts","sourceRoot":"","sources":["../../src/hooks/useKeyboardResize.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAIvD;;GAEG;AACH,MAAM,WAAW,wBAAwB;IACvC,SAAS,EAAE,SAAS,CAAC;IACrB,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC1B,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC/B,QAAQ,CAAC,EAAE,CAAC,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE,KAAK,EAAE,WAAW,KAAK,IAAI,CAAC,GAAG,SAAS,CAAC;IACvE,WAAW,CAAC,EAAE,CAAC,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE,KAAK,EAAE,WAAW,KAAK,IAAI,CAAC,GAAG,SAAS,CAAC;CAC3E;AAKD;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,wBAAwB;kCAgBhD,MAAM,SAAS,KAAK,CAAC,aAAa;EAiKpD"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { Size } from '../types';
|
|
2
|
+
export interface UsePaneSizeOptions {
|
|
3
|
+
defaultSize?: Size;
|
|
4
|
+
size?: Size;
|
|
5
|
+
minSize?: Size;
|
|
6
|
+
maxSize?: Size;
|
|
7
|
+
containerSize: number;
|
|
8
|
+
}
|
|
9
|
+
export interface UsePaneSizeResult {
|
|
10
|
+
pixelSize: number;
|
|
11
|
+
minPixelSize: number;
|
|
12
|
+
maxPixelSize: number;
|
|
13
|
+
isControlled: boolean;
|
|
14
|
+
}
|
|
15
|
+
export declare function usePaneSize(options: UsePaneSizeOptions): UsePaneSizeResult;
|
|
16
|
+
//# sourceMappingURL=usePaneSize.d.ts.map
|