first commit
This commit is contained in:
163
docs/ui/react/react-ui/src/components/resizable/resizable.md
Normal file
163
docs/ui/react/react-ui/src/components/resizable/resizable.md
Normal 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).
|
||||
Reference in New Issue
Block a user