first commit

This commit is contained in:
2026-05-29 08:20:56 +01:00
commit 949299f58d
6368 changed files with 527176 additions and 0 deletions

View File

@@ -0,0 +1,163 @@
# Resizable
<!-- begin:auto-docs -->
_(no submodules)_
<!-- end:auto-docs --> - resizable.variants — `resizablePanelGroupVariants`,…
- [resizable.variants](./resizable.variants.md) — `resizablePanelGroupVariants`, `resizablePanelVariants`, `resizableHandleVariants`, and `resizableHandleGripVariants` a…
- resizable.variants — `resizablePanelGroupVariants`,…
- [resizable.variants](./resizable.variants.md) — `resizablePanelGroupVariants`, `resizablePanelVariants`, `resizableHandleVariants`, and `resizableHandleGripVariants` a…
- resizable.variants — `resizablePanelGroupVariants`,…
- [resizable.variants](./resizable.variants.md) — `resizablePanelGroupVariants`, `resizablePanelVariants`, `resizableHandleVariants`, and `resizableHandleGripVariants` a…
- resizable.variants — `resizablePanelGroupVariants`,…
- [resizable.variants](./resizable.variants.md) — `resizablePanelGroupVariants`, `resizablePanelVariants`, `resizableHandleVariants`, and `resizableHandleGripVariants` a…
- [resizable.variants](./resizable.variants.md) — `resizablePanelGroupVariants`, `resizablePanelVariants`, `resizableHandleVariants`, and `resizableHandleGripVariants` a…
## Purpose
`<ResizablePanelGroup>`, `<ResizablePanel>`, and `<ResizableHandle>` are the chassis split-layout primitives built on `react-resizable-panels` v4 (`Group` / `Panel` / `Separator`). The library renders a flex container with `orientation` driving `flex-direction` (`horizontal` → row, `vertical` → column) and exposes pointer + keyboard resize gestures (Arrow keys when the handle is focused). The chassis layers cva slots and an optional `withHandle` indicator on the separator.
Use this primitive for split-layout surfaces (sidebar / main / preview, master-detail, code-editor + preview). Each panel sizes itself via flex-grow / flex-shrink driven by the library; consumers persist sizes via the `defaultLayout` / `onLayoutChanged` props that pass through to the library.
## Features
- Built on `react-resizable-panels` v4 (`Group` / `Panel` / `Separator`).
- `orientation` prop on the group: `"horizontal"` (default; row layout) or `"vertical"` (column layout).
- Pointer drag + keyboard resize (ArrowLeft/Right or ArrowUp/Down when the handle is focused) supplied by the library.
- `defaultSize`, `minSize`, `maxSize`, `collapsible` per panel.
- `withHandle` opt-in grip indicator on `<ResizableHandle>` so coarse pointers (touch) get a visible hit indicator.
- `defaultLayout` + `onLayoutChanged` for persisting sizes between sessions.
- Theme-driven cva overrides for `ResizablePanelGroup`, `ResizablePanel`, `ResizableHandle`, `ResizableHandleGrip`.
- `data-disabled` on the handle when the group's `disabled` prop is true; the handle still mounts but the cursor and opacity collapse so users see the disabled affordance.
## Usage: Basic Example
```tsx
import {
ResizableHandle,
ResizablePanel,
ResizablePanelGroup,
} from "@suaveplan/react-ui";
export function TwoPaneSplit() {
return (
<ResizablePanelGroup id="main-split" className="rounded border">
<ResizablePanel id="sidebar" defaultSize={25} minSize={15}>
<div className="p-3">Sidebar</div>
</ResizablePanel>
<ResizableHandle id="handle-1" withHandle />
<ResizablePanel id="main" defaultSize={75} minSize={40}>
<div className="p-3">Main content</div>
</ResizablePanel>
</ResizablePanelGroup>
);
}
```
## Usage: Advanced Example
```tsx
import {
ResizableHandle,
ResizablePanel,
ResizablePanelGroup,
} from "@suaveplan/react-ui";
import type { Layout } from "react-resizable-panels";
const STORAGE_KEY = "editor.layout";
function loadLayout(): Layout | undefined {
try {
const raw = localStorage.getItem(STORAGE_KEY);
return raw ? (JSON.parse(raw) as Layout) : undefined;
} catch {
return undefined;
}
}
function saveLayout(layout: Layout): void {
try {
localStorage.setItem(STORAGE_KEY, JSON.stringify(layout));
} catch {
/* ignore */
}
}
export function EditorSplit() {
return (
<ResizablePanelGroup
id="editor-split"
defaultLayout={loadLayout()}
onLayoutChanged={saveLayout}
className="h-screen"
>
<ResizablePanel id="files" defaultSize={20} minSize={15} collapsible>
<FileTree />
</ResizablePanel>
<ResizableHandle id="h1" withHandle />
<ResizablePanel id="editor" defaultSize={50} minSize={30}>
<CodeEditor />
</ResizablePanel>
<ResizableHandle id="h2" withHandle />
<ResizablePanel id="preview" defaultSize={30} minSize={20}>
<LivePreview />
</ResizablePanel>
</ResizablePanelGroup>
);
}
```
## API Reference
### `<ResizablePanelGroup>`
Wraps `Group` from `react-resizable-panels`.
| Prop | Type | Default | Description |
|---|---|---|---|
| `id` | `string \| number` | `useId()` | Unique identifier within the application. Falls back to React's `useId()` when omitted. |
| `orientation` | `"horizontal" \| "vertical"` | `"horizontal"` | Layout axis. |
| `defaultLayout` | `Layout` (`{ [panelId]: number }`) | _none_ | Initial layout (percentages 0..100 keyed by panel id). |
| `onLayoutChange` | `(layout: Layout) => void` | _none_ | Fires on every pointer-move during drag — debounce before persisting. |
| `onLayoutChanged` | `(layout: Layout) => void` | _none_ | Fires once after the pointer is released — recommended for persistence. |
| `disabled` | `boolean` | `false` | Disable resize across the entire group; the handle gets `data-disabled` and the cursor stays default. |
| `disableCursor` | `boolean` | `false` | Disable the library's drag-cursor styling. |
### `<ResizablePanel>`
Wraps `Panel`.
| Prop | Type | Default | Description |
|---|---|---|---|
| `id` | `string \| number` | `useId()` | Unique identifier within the group. |
| `defaultSize` | `number \| string` | _auto_ | Initial size as a percentage (`0..100`) or a CSS value. |
| `minSize` | `number \| string` | _none_ | Minimum size during resize. |
| `maxSize` | `number \| string` | _none_ | Maximum size during resize. |
| `collapsible` | `boolean` | `false` | Allow the panel to collapse below `minSize` and snap to zero. |
| `onResize` | `(size, id, prev) => void` | _none_ | Fires when the panel's size changes. |
### `<ResizableHandle>`
Wraps `Separator`.
| Prop | Type | Default | Description |
|---|---|---|---|
| `id` | `string \| number` | `useId()` | Unique identifier within the group. |
| `withHandle` | `boolean` | `false` | Render a small grip square (using lucide's `<GripVerticalIcon>`) centred on the handle. |
| `disabled` | `boolean` | `false` | Disable this individual handle. |
| `disableDoubleClick` | `boolean` | `false` | Disable double-click-to-collapse on this handle. |
## Implementation Notes
- The library v4 names are `Group` / `Panel` / `Separator`; the chassis re-exports them as `<ResizablePanelGroup>` / `<ResizablePanel>` / `<ResizableHandle>` for naming parity with the upstream shadcn registry name and so the chassis surface reads naturally.
- The library applies `display`, `flex-direction`, `flex-wrap`, and `overflow` styles inline on the group root and CANNOT be overridden via className — the chassis cva sticks to layout-neutral recipes (`h-full w-full`).
- The chassis NEVER assigns a numeric `z-index` (per the shadcn skill rule). Stacking is implicit — the handle sits between two panels in document order.
- Each panel renders as an OUTER `<div data-panel>` wrapping a chassis-className-styled INNER `<div>`. Tests inspect the inner element when asserting cva classes.
- The handle exposes `aria-orientation` perpendicular to the resize axis (a horizontal group has a vertical separator), matching the WAI-ARIA separator pattern.
- The `withHandle` grip uses lucide's `<GripVerticalIcon>`; consumers can render a custom indicator by composing their own children inside `<ResizableHandle>` instead of opting into `withHandle`.
- The `disabled` group prop sets `data-disabled` on the handle; the cva collapses the cursor and reduces opacity so users see the disabled state without losing the focus ring.
- Per the WAI-ARIA separator pattern, the handle is keyboard-focusable (`tabindex="0"`); ArrowLeft/Right or ArrowUp/Down resize the adjacent panels by 5% per keystroke (library default).