React Multi Split Pane
Fork of react-split-pane with support for more than two panes.
Installing
npm install react-multi-split-pane
yarn add react-multi-split-pane
Example Usage
<SplitPane split="vertical" minSize={50}>
<div></div>
<div></div>
<div></div>
</SplitPane>
<SplitPane split="vertical" minSize={50}>
<div></div>
<SplitPane split="horizontal">
<div></div>
<div></div>
</SplitPane>
</SplitPane>
Much of the code has been rewritten, so the feature set is a bit different. All
code has been converted to TypeScript.
The most important changes:
- All pane sizes are relative, so when the window is resized, all panes
grow/shrink at the same time (as far as their
minSize
properties allow) - An array of all pane sizes is passed to the
onDragFinished
and onChange
callbacks
Props
split: 'vertical' | 'horizontal'
Split direction, defaults to vertical.
defaultSizes: number[]
Array of (relative) default sizes for the individual panes. Missing values
default to 1. When no defaultSizes
are passed, all sizes default to 1,
equally distributing the available space (as far as minSize
values permit).
minSize: number | number[]
Minimum size of all panes (in pixels), or array containing individual minimum
sizes for each pane. Defaults to 50.
className: string
Additional CSS class name that is appied to all elements rendered by the
SplitPane. For a class name custom
, the individual elements can be selected as
.SplitPane.custom
, .Resizer.custom
, and .Pane.custom
.
resizerClassName: string
Additional CSS class name that is appied only to the resizer rendered by the
SplitPane.
onDragStarted: () => void
This callback is invoked when a drag starts.
onDragFinished: (sizes: number[]) => void
This callback is invoked when a drag ends.
onChange: (sizes: number[]) => void
This callback is invoked with the current drag during a drag event. It is
recommended that it is wrapped in a debounce function.
Persisting Positions
Each SplitPane accepts an onChange function prop. Used in conjunction with
defaultSize and a persistence layer, you can ensure that your splitter choices
survive a refresh of your app.
For example, if you are comfortable with the trade-offs of localStorage, you
could do something like the following:
<SplitPane
split="vertical" minSize={50}
defaultSizes={JSON.parse(localStorage.getItem('splitPos')) || undefined}
onDragFinished={(size) => localStorage.setItem('splitPos', JSON.stringify(size))}
>
<div></div>
<div></div>
</SplitPane>
Example styling
This gives a single pixel wide divider, but with a 'grabbable' surface of 11
pixels.
Thanks to background-clip: padding-box;
for making transparent borders
possible.
.Resizer {
background: #000;
opacity: .2;
z-index: 1;
box-sizing: border-box;
background-clip: padding-box;
}
.Resizer:hover {
transition: all 2s ease;
}
.Resizer.horizontal {
height: 11px;
margin: -5px 0;
border-top: 5px solid rgba(255, 255, 255, 0);
border-bottom: 5px solid rgba(255, 255, 255, 0);
cursor: row-resize;
}
.Resizer.horizontal:hover, .Resizer.horizontal.resizing {
border-top: 5px solid rgba(0, 0, 0, 0.5);
border-bottom: 5px solid rgba(0, 0, 0, 0.5);
}
.Resizer.vertical {
width: 11px;
margin: 0 -5px;
border-left: 5px solid rgba(255, 255, 255, 0);
border-right: 5px solid rgba(255, 255, 255, 0);
cursor: col-resize;
}
.Resizer.vertical:hover, .Resizer.vertical.resizing {
border-left: 5px solid rgba(0, 0, 0, 0.5);
border-right: 5px solid rgba(0, 0, 0, 0.5);
}
.DragLayer {
z-index: 1;
pointer-events: none;
}
.DragLayer.resizing {
pointer-events: auto;
}
.DragLayer.horizontal {
cursor: row-resize;
}
.DragLayer.vertical {
cursor: col-resize;
}