KQ-1282 · FLOAT/PANEL
Float Panel
A floating inspector over a workspace: dragged by its title bar it follows 1:1, a magnet pulls it flush to an edge on springs.flick, pushed into a side it docks full height on springs.snap with the throw's velocity while the workspace glides aside, and carried into a corner it collapses into a pill that grows back into the panel when pressed. Its edges resize it 1:1 and a release springs to the nearest preset width on snap; with storageKey each key remembers its own place. The title bar is a button whose arrows nudge, Alt with an arrow docks, floats or collapses, Enter docks or floats and Escape collapses; the edge is a separator with arrow, Home and End keys.
Pump house · drag the inspector by its title
Install
One command — the source lands in your repo. Or copy it from the Code tab.
$ pnpm dlx shadcn@latest add @kinetiq/float-panel
Props
- title
- string
- default "Inspector"
- The title bar's text, the pill's label and the panel's accessible name.
- icon
- ReactNode
- default a sliders icon
- 16px, drawn in currentColor, in the title bar and on the pill.
- children
- ReactNode
- default —
- The inspector's body; it scrolls when it is taller than the room.
- workspace
- ReactNode
- default a dotted canvas
- What the panel floats over; a docked panel takes room from it.
- placement / defaultPlacement
- FloatPanelPlacement
- default { mode: "float", x: 1, y: 0, width: 280 }
- Floating at shares of the free room ({ mode: "float", x, y, width }), docked to a side ({ mode: "dock", side, width }) or collapsed to a corner ({ mode: "pill", corner }); controlled or initial.
- onPlacementChange
- (placement: FloatPanelPlacement) => void
- default —
- Fires from the drag, key or button that moved, docked, collapsed or resized it.
- storageKey
- string
- default —
- Remember the placement under this key in local storage; each key keeps its own place, read the same way on the server and in the browser.
- magnet
- number
- default 24
- How close an edge has to come, in px, before the panel snaps flush to it; 0 turns the magnet off.
- collapse
- number
- default 64
- How close to a corner, in px, a drag has to come to collapse the panel into a pill there; 0 never collapses by drag.
- widths
- number | number[]
- default 3
- Preset widths a resize springs to: a count spread evenly between minWidth and maxWidth (0 resizes freely), or the widths in px.
- minWidth
- number
- default 220
- Narrowest the panel can be, in px.
- maxWidth
- number
- default 380
- Widest the panel can be, in px; the container caps it too.
- nudge
- number
- default 8
- How far one arrow key moves the panel, in px; Shift moves four times as far.
- reserve
- boolean
- default true
- A docked panel takes its room from the workspace instead of covering it, while at least 240 px of workspace remain.
- badge
- ReactNode
- default —
- Shown on the pill after the title: a count, a dot.
- height
- number | string
- default 440
- The container's height, in px or any CSS length.
- label
- string
- default "Workspace"
- The workspace region's accessible name.
- accent
- string
- default "var(--accent-bright)"
- The dock and pill ghosts, the preset guides, the grip and the width readout; any CSS colour.
- sound
- boolean
- default false
- A snap when the magnet catches and when a width lands on a preset, a thock when it docks, collapses, expands or a pill lands.
- disabled
- boolean
- default false
- Inert and dimmed: no drags, keys or buttons.
- className
- string
- default —
- Classes for the container.
| Prop | Type | Default | Description |
|---|---|---|---|
| title | string | "Inspector" | The title bar's text, the pill's label and the panel's accessible name. |
| icon | ReactNode | a sliders icon | 16px, drawn in currentColor, in the title bar and on the pill. |
| children | ReactNode | — | The inspector's body; it scrolls when it is taller than the room. |
| workspace | ReactNode | a dotted canvas | What the panel floats over; a docked panel takes room from it. |
| placement / defaultPlacement | FloatPanelPlacement | { mode: "float", x: 1, y: 0, width: 280 } | Floating at shares of the free room ({ mode: "float", x, y, width }), docked to a side ({ mode: "dock", side, width }) or collapsed to a corner ({ mode: "pill", corner }); controlled or initial. |
| onPlacementChange | (placement: FloatPanelPlacement) => void | — | Fires from the drag, key or button that moved, docked, collapsed or resized it. |
| storageKey | string | — | Remember the placement under this key in local storage; each key keeps its own place, read the same way on the server and in the browser. |
| magnet | number | 24 | How close an edge has to come, in px, before the panel snaps flush to it; 0 turns the magnet off. |
| collapse | number | 64 | How close to a corner, in px, a drag has to come to collapse the panel into a pill there; 0 never collapses by drag. |
| widths | number | number[] | 3 | Preset widths a resize springs to: a count spread evenly between minWidth and maxWidth (0 resizes freely), or the widths in px. |
| minWidth | number | 220 | Narrowest the panel can be, in px. |
| maxWidth | number | 380 | Widest the panel can be, in px; the container caps it too. |
| nudge | number | 8 | How far one arrow key moves the panel, in px; Shift moves four times as far. |
| reserve | boolean | true | A docked panel takes its room from the workspace instead of covering it, while at least 240 px of workspace remain. |
| badge | ReactNode | — | Shown on the pill after the title: a count, a dot. |
| height | number | string | 440 | The container's height, in px or any CSS length. |
| label | string | "Workspace" | The workspace region's accessible name. |
| accent | string | "var(--accent-bright)" | The dock and pill ghosts, the preset guides, the grip and the width readout; any CSS colour. |
| sound | boolean | false | A snap when the magnet catches and when a width lands on a preset, a thock when it docks, collapses, expands or a pill lands. |
| disabled | boolean | false | Inert and dimmed: no drags, keys or buttons. |
| className | string | — | Classes for the container. |