Skip to content

Commit c238995

Browse files
committed
Implement react-split-pane v3.0.0-alpha.1
Complete rewrite with modern React patterns, TypeScript, and accessibility. Core Features: - Hooks-based architecture (useResizer, useKeyboardResize, usePaneSize) - Full TypeScript support with strict mode - Comprehensive accessibility (ARIA, keyboard nav, screen reader) - Mouse, touch, and keyboard resize support - Controlled/uncontrolled modes - Persistence hook for localStorage/sessionStorage - Tree-shakeable exports (core, keyboard, persistence) - RAF-throttled resize for 60fps performance - Support for 2+ panes with nested layouts - Snap points and step-based resizing Components: - SplitPane: Main container with direction, resize callbacks - Pane: Individual panes with size constraints - Divider: Accessible separator with keyboard support Breaking Changes from v0.1: - New component-based API (props on Pane instead of SplitPane) - Renamed props (split->direction, allowResize->resizable) - Event handlers return sizes array instead of single value - Removed IE11 support - Removed react-lifecycles-compat dependency Build System: - Rollup 4.x with modern plugins - ESM + CJS outputs with source maps - Vitest for testing with 90%+ coverage targets - ESLint with TypeScript, React, and a11y rules Testing: - Vitest + React Testing Library - Unit tests for components and utilities - Mocked ResizeObserver and RAF Documentation: - Comprehensive README with examples - API reference with all props - Migration guide from v0.1 - Default styles with dark mode support Bundle Target: <5KB gzipped Test Coverage: 90%+ (configured in vitest.config) Node Requirement: >=18.0.0 React Support: ^17.0.0 || ^18.0.0
1 parent 518017d commit c238995

23 files changed

+2059
-0
lines changed

v3/.eslintrc.json

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
{
2+
"parser": "@typescript-eslint/parser",
3+
"parserOptions": {
4+
"ecmaVersion": 2020,
5+
"sourceType": "module",
6+
"ecmaFeatures": {
7+
"jsx": true
8+
}
9+
},
10+
"extends": [
11+
"eslint:recommended",
12+
"plugin:@typescript-eslint/recommended",
13+
"plugin:react/recommended",
14+
"plugin:react-hooks/recommended",
15+
"plugin:jsx-a11y/recommended"
16+
],
17+
"plugins": [
18+
"@typescript-eslint",
19+
"react",
20+
"react-hooks",
21+
"jsx-a11y"
22+
],
23+
"rules": {
24+
"react/react-in-jsx-scope": "off",
25+
"react/prop-types": "off",
26+
"@typescript-eslint/explicit-module-boundary-types": "off",
27+
"@typescript-eslint/no-explicit-any": "error",
28+
"react-hooks/rules-of-hooks": "error",
29+
"react-hooks/exhaustive-deps": "warn",
30+
"jsx-a11y/no-static-element-interactions": "warn"
31+
},
32+
"settings": {
33+
"react": {
34+
"version": "detect"
35+
}
36+
}
37+
}

v3/.gitignore

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
node_modules
2+
dist
3+
coverage
4+
.DS_Store
5+
*.log
6+
.vscode
7+
.idea
8+
*.tsbuildinfo

v3/README.md

Lines changed: 295 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,295 @@
1+
# React Split Pane v3
2+
3+
Modern, accessible, TypeScript-first split pane component for React.
4+
5+
[![NPM version](https://img.shields.io/npm/v/react-split-pane.svg?style=flat)](https://www.npmjs.com/package/react-split-pane)
6+
![NPM license](https://img.shields.io/npm/l/react-split-pane.svg?style=flat)
7+
[![Bundle size](https://img.shields.io/bundlephobia/minzip/react-split-pane)](https://bundlephobia.com/package/react-split-pane)
8+
9+
## ✨ Features
10+
11+
- 🪝 **Hooks-based** - Built with modern React patterns
12+
- 📘 **TypeScript** - Full type safety out of the box
13+
-**Accessible** - Keyboard navigation, ARIA attributes, screen reader support
14+
- 📱 **Touch-friendly** - Full mobile/tablet support
15+
- 🎯 **Flexible** - Controlled/uncontrolled modes, nested layouts, 2+ panes
16+
- 🪶 **Lightweight** - < 5KB gzipped
17+
-**Performant** - RAF-throttled resize, optimized renders
18+
- 🎨 **Customizable** - Full styling control
19+
20+
## Installation
21+
22+
```bash
23+
npm install react-split-pane@next
24+
25+
# or
26+
yarn add react-split-pane@next
27+
28+
# or
29+
pnpm add react-split-pane@next
30+
```
31+
32+
## Quick Start
33+
34+
```tsx
35+
import { SplitPane, Pane } from 'react-split-pane';
36+
37+
function App() {
38+
return (
39+
<SplitPane direction="horizontal">
40+
<Pane minSize="200px" defaultSize="300px">
41+
<Sidebar />
42+
</Pane>
43+
<Pane>
44+
<MainContent />
45+
</Pane>
46+
</SplitPane>
47+
);
48+
}
49+
```
50+
51+
## Basic Usage
52+
53+
### Horizontal Split (Side-by-Side)
54+
55+
```tsx
56+
<SplitPane direction="horizontal">
57+
<Pane defaultSize="25%">
58+
<LeftPanel />
59+
</Pane>
60+
<Pane>
61+
<RightPanel />
62+
</Pane>
63+
</SplitPane>
64+
```
65+
66+
### Vertical Split (Top-Bottom)
67+
68+
```tsx
69+
<SplitPane direction="vertical">
70+
<Pane defaultSize="100px">
71+
<Header />
72+
</Pane>
73+
<Pane>
74+
<Content />
75+
</Pane>
76+
</SplitPane>
77+
```
78+
79+
### Controlled Mode
80+
81+
```tsx
82+
function App() {
83+
const [sizes, setSizes] = useState([300, 500]);
84+
85+
return (
86+
<SplitPane onResize={setSizes}>
87+
<Pane size={sizes[0]} minSize="200px">
88+
<Sidebar />
89+
</Pane>
90+
<Pane size={sizes[1]}>
91+
<Main />
92+
</Pane>
93+
</SplitPane>
94+
);
95+
}
96+
```
97+
98+
### Nested Layouts
99+
100+
```tsx
101+
<SplitPane direction="vertical">
102+
<Pane defaultSize="60px">
103+
<Header />
104+
</Pane>
105+
106+
<SplitPane direction="horizontal">
107+
<Pane defaultSize="250px" minSize="150px">
108+
<Sidebar />
109+
</Pane>
110+
111+
<SplitPane direction="vertical">
112+
<Pane>
113+
<Editor />
114+
</Pane>
115+
<Pane defaultSize="200px">
116+
<Console />
117+
</Pane>
118+
</SplitPane>
119+
</SplitPane>
120+
</SplitPane>
121+
```
122+
123+
## Advanced Features
124+
125+
### Persistence
126+
127+
Save pane sizes to localStorage:
128+
129+
```tsx
130+
import { usePersistence } from 'react-split-pane/persistence';
131+
132+
function App() {
133+
const [sizes, setSizes] = usePersistence({ key: 'my-layout' });
134+
135+
return (
136+
<SplitPane onResize={setSizes}>
137+
<Pane size={sizes[0] || 300}>
138+
<Sidebar />
139+
</Pane>
140+
<Pane size={sizes[1]}>
141+
<Main />
142+
</Pane>
143+
</SplitPane>
144+
);
145+
}
146+
```
147+
148+
### Snap Points
149+
150+
```tsx
151+
<SplitPane
152+
snapPoints={[200, 400, 600]}
153+
snapTolerance={20}
154+
>
155+
{/* panes */}
156+
</SplitPane>
157+
```
158+
159+
### Custom Divider
160+
161+
```tsx
162+
function CustomDivider(props) {
163+
return (
164+
<div {...props} style={{ ...props.style, background: 'blue' }}>
165+
<GripIcon />
166+
</div>
167+
);
168+
}
169+
170+
<SplitPane divider={CustomDivider}>
171+
{/* panes */}
172+
</SplitPane>
173+
```
174+
175+
## Keyboard Navigation
176+
177+
The divider is fully keyboard accessible:
178+
179+
- **Arrow Keys**: Resize by `step` pixels (default: 10px)
180+
- **Shift + Arrow**: Resize by larger step (default: 50px)
181+
- **Home**: Minimize left/top pane
182+
- **End**: Maximize left/top pane
183+
- **Tab**: Navigate between dividers
184+
185+
## API Reference
186+
187+
### SplitPane Props
188+
189+
| Prop | Type | Default | Description |
190+
|------|------|---------|-------------|
191+
| `direction` | `'horizontal' \| 'vertical'` | `'horizontal'` | Layout direction |
192+
| `resizable` | `boolean` | `true` | Whether panes can be resized |
193+
| `snapPoints` | `number[]` | `[]` | Snap points in pixels |
194+
| `snapTolerance` | `number` | `10` | Snap tolerance in pixels |
195+
| `step` | `number` | `10` | Keyboard resize step |
196+
| `onResizeStart` | `(event) => void` | - | Called when resize starts |
197+
| `onResize` | `(sizes, event) => void` | - | Called during resize |
198+
| `onResizeEnd` | `(sizes, event) => void` | - | Called when resize ends |
199+
| `className` | `string` | - | CSS class name |
200+
| `style` | `CSSProperties` | - | Inline styles |
201+
| `divider` | `ComponentType` | - | Custom divider component |
202+
| `dividerClassName` | `string` | - | Divider class name |
203+
| `dividerStyle` | `CSSProperties` | - | Divider inline styles |
204+
205+
### Pane Props
206+
207+
| Prop | Type | Default | Description |
208+
|------|------|---------|-------------|
209+
| `defaultSize` | `string \| number` | `'50%'` | Initial size (uncontrolled) |
210+
| `size` | `string \| number` | - | Controlled size |
211+
| `minSize` | `string \| number` | `0` | Minimum size |
212+
| `maxSize` | `string \| number` | `Infinity` | Maximum size |
213+
| `className` | `string` | - | CSS class name |
214+
| `style` | `CSSProperties` | - | Inline styles |
215+
216+
## Styling
217+
218+
### Basic Styles
219+
220+
```css
221+
.split-pane {
222+
height: 100vh;
223+
}
224+
225+
.split-pane-divider {
226+
background: #e0e0e0;
227+
transition: background 0.2s;
228+
}
229+
230+
.split-pane-divider:hover {
231+
background: #b0b0b0;
232+
}
233+
234+
.split-pane-divider:focus {
235+
outline: 2px solid #2196f3;
236+
outline-offset: -2px;
237+
}
238+
```
239+
240+
### Custom Divider Styles
241+
242+
```css
243+
.split-pane-divider.horizontal {
244+
width: 1px;
245+
margin: 0;
246+
background: linear-gradient(to right, transparent, #ccc, transparent);
247+
}
248+
249+
.split-pane-divider.vertical {
250+
height: 1px;
251+
margin: 0;
252+
background: linear-gradient(to bottom, transparent, #ccc, transparent);
253+
}
254+
```
255+
256+
## Migration from v0.1.x
257+
258+
See [MIGRATION.md](./MIGRATION.md) for detailed migration guide.
259+
260+
**Quick changes:**
261+
262+
```tsx
263+
// v0.1.x
264+
<SplitPane split="vertical" minSize={50} defaultSize={100}>
265+
<div>Pane 1</div>
266+
<div>Pane 2</div>
267+
</SplitPane>
268+
269+
// v3
270+
<SplitPane direction="horizontal">
271+
<Pane minSize="50px" defaultSize="100px">
272+
<div>Pane 1</div>
273+
</Pane>
274+
<Pane>
275+
<div>Pane 2</div>
276+
</Pane>
277+
</SplitPane>
278+
```
279+
280+
## Browser Support
281+
282+
- Chrome/Edge (latest 2 versions)
283+
- Firefox (latest 2 versions)
284+
- Safari (latest 2 versions)
285+
- Mobile browsers (iOS Safari, Chrome Android)
286+
287+
**Note:** IE11 is not supported. Use v0.1.x for IE11 compatibility.
288+
289+
## Contributing
290+
291+
Contributions are welcome! Please see [CONTRIBUTING.md](../../CONTRIBUTING.md).
292+
293+
## License
294+
295+
MIT © [tomkp](https://github.com/tomkp)

0 commit comments

Comments
 (0)