Floor planner
ProDesk booking across floors. The floors really are stacked: choose one and the floors above it lift away, so its plan is unobstructed, then book a desk from the plan. Every desk has a name and a state, and the side card says each choice in words.
pnpm dlx shadcn@latest add @caliper/floor-plannerChoose a free desk to book it.
12 of 18 desks free on Floor 2. Arrow keys move between desks.
When to use #
Use it for
- Hot-desking, room and seat booking where people think in floors of a building.
- Showing which part of a building a booking sits in, with the plan as the way to choose.
Skip it when
- A single flat room; use the seat picker.
- Time slots within a day; use the availability scheduler.
Decisions #
Why it is built this way, so you know what to keep when you change your copy.
Behaviour
- The parent owns the bookings
- Pass floors, desks and bookings, and answer onBook and onCancel. The part draws the plan, keeps the choice and announces it; it never decides who may book.
- Desks are one tab stop
- Arrow keys move between desks, Home and End jump along a row, and a booked or blocked desk is named with its state but cannot be pressed.
Usage
- Narrow means flat
- Under about 460px the plan lies flat and shows the chosen floor only, so every desk is a 44px target. The stack is for rooms wide enough to read it.
Installation #
pnpm dlx shadcn@latest add @caliper/floor-planner
Adds its 6 files to components/ui/, each with its own stylesheet, and installs their packages. Pro installs with your access token, set up once per project. See Pro access. The first part also adds Caliper UI’s tokens; import them once as in Installation.
Usage #
Import it from your copy of the source. The example behind the preview comes with the source.
import { FloorPlanner } from "@/components/ui/floor-planner"
The worked example comes with Pro
The 56-line example behind the preview opens here with a Pro or Team licence.
API reference #
FloorPlanner
component
Accessibility #
It is built on native elements and keeps aria-hidden, aria-label, aria-disabled, aria-pressed and aria-live in step with state. Changes that matter are announced through a live region.
Motion #
It declares no animation in its component source. Any transition comes from its stylesheet, which uses the duration tokens that collapse to instant under prefers-reduced-motion.