Skip to content

Panes

Divides a box into regions with a draggable bar between each pair. Use it for an editor beside a file list, a preview beside a form, or any split whose proportions the reader decides.

tsx
import { Pane, Panes } from 'neba';

<Panes>
  <Pane defaultSize="240px" minSize="160px">
    Files
  </Pane>
  <Pane>Editor</Pane>
</Panes>;

Panes fills the box it is in, so give that box a height.

Props

Panes

PropTypeDefaultDescription
orientationshared'horizontal' | 'vertical''horizontal'Which way the panes run. horizontal puts them side by side with upright handles between them; vertical stacks them
resizablebooleantrueWhether the handles can be dragged. Turn it off for a split that is a layout rather than a control
sizeshared'xs' | 'sm' | 'md' | 'lg' | 'xl''md'A handle's thickness, which is the width of the target the pointer has to hit. What is drawn is a hairline; what can be grabbed is wider
colorshared'primary' | 'secondary' | 'success' | 'warning' | 'danger' | 'info''primary'The family the handles light up in. A Panes draws no sheet, so the colour only shows in the hairline and the focus ring
onResize(sizes: number[]) => voidFires with every pane's share, in percent, while a handle is dragged
onResizeEnd(sizes: number[]) => voidFires once, with the same shape, when the handle is let go
childrenReactNodeThe Panes. The constraints are read off the children’s props, so the direct children have to be Panes: a Pane wrapped in something else is a pane with no minimum

Every other <div> attribute passes through. The direct children have to be Panes: the constraints are read off their props. The shared axes are in prop conventions.

Pane

PropTypeDefaultDescription
defaultSizenumber | stringThe share this pane starts with. A number is a percentage; a string is a length (240px, 15rem, 20%). Panes with no defaultSize split what is left equally
minSizenumber | string0How small it may be dragged, which is also its neighbour's ceiling
maxSizenumber | stringHow large it may be dragged. Unbounded when left out
childrenReactNodeWhat is inside the pane. A Pane carries no surface of its own, so put a Box or a Card in it when one is wanted

Every other <div> attribute passes through.

Examples

orientation

horizontal lays the panes out side by side with upright bars between them; vertical stacks them with bars across. A bar always runs across the axis the panes run along.

defaultSize, minSize and maxSize

All three take a number, read as a percentage, or a CSS length: '240px', '15rem', '20%'. Panes with no defaultSize split whatever is left over equally. A pane's minSize is also its neighbour's ceiling, so a drag stops at whichever bound it reaches first.

onResize reports every pane's share in percent while the bar moves; onResizeEnd fires once when it is let go.

Nesting

A Panes inside a Pane is a split inside a split, which is how a three-region layout is built. Give the inner one the other orientation.

tsx
<Panes>
  <Pane defaultSize="240px">Files</Pane>
  <Pane>
    <Panes orientation="vertical">
      <Pane defaultSize={70}>Editor</Pane>
      <Pane>Terminal</Pane>
    </Panes>
  </Pane>
</Panes>

resizable

resizable={false} leaves the bar as a rule between the panes: it is still drawn, but it cannot be dragged and it is out of the tab order.

Accessibility

  • Each bar is a separator with aria-valuenow carrying the share, in percent, of the pane in front of it.
  • A bar takes focus. ArrowLeft and ArrowRight move a upright bar; ArrowUp and ArrowDown move one that lies across.
  • aria-orientation describes the bar, not the panes: an upright bar between panes that run across is vertical.
  • A drag under RTL moves the boundary the way the pointer went.

Released under the MIT License