Skip to content

FloorPlan

Open .md
Live preview

Loading example…

A salon plan on a logical-unit canvas, read-only unless editable. The canvas fills its container and keeps the width x height aspect ratio, and every item is placed in percentages of it, so hit areas always match what is drawn. Items are buttons in array order with roving arrow-key focus. A state shows as an icon plus text (never colour alone), late and selected as one ring around the whole item, and the label moves under the shape when the table is too small to hold it.

import { FloorPlan } from "@jcsoftdev/ui/containers";

Single-component entry points tree-shake to just this component: @jcsoftdev/ui/containers/floor-plan.

<FloorPlan
width={36}
height={26}
items={plan.items}
landmarks={plan.landmarks}
/>
Prop Type Default Description
width * number Canvas size in grid units. The canvas fills its container and keeps this aspect ratio.
height * number
items * readonly FloorPlanItem[]
landmarks readonly FloorPlanLandmark[] []
selectedId string | null Controlled selection; the item gets the selected ring and aria-pressed.
onSelect ((id: string) => void) Called with the id of the item or landmark that was picked, by click, keyboard or the start of a drag.
editable boolean false Lets people rearrange the plan: drag an item or landmark to move it, drag a corner handle of the selected one to resize, press the rotate button or r to turn it a quarter, Alt with an arrow to nudge and Alt and Shift with an arrow to resize. Edits snap to `…
onChange ((items: FloorPlanItem[], landmarks: FloorPlanLandmark[]) => void) Receives the full lists with the edited box applied; called once per drop and once per key press.
snap number 1 Grid step in units that positions and sizes snap to while editing. Defaults to 1.
minSize number 1 Smallest width or height in units while editing. Defaults to 1.
aria-label string Accessible name of the plan. Defaults to the localized “Floor plan”.
className string
classNames Partial<Record<FloorPlanPart, string>> Per-part classes merged with cn; consumer classes win. className still targets the root and is merged last.

* marks a required prop.

packages/ui/src/containers/floor-plan/floor-plan.tsx