Install dependencies and run the demo from the monorepo root:
pnpm install pnpm dev
Import the editor in your Next.js or React app:
import {
FloorPlanEditor,
createEmptyFloorPlan,
} from "@ticketss/floor-plan-editor";
import "@ticketss/floor-plan-editor/styles/tokens.css";Full install and publish steps — expanded in later milestones.
| Prop | Type | Description |
|---|---|---|
value | FloorPlanDocument | Controlled floor plan state |
onChange | (doc) => void | Called when the document changes |
isDarkMode | boolean | Sets data-theme="dark" on editor root |
lang | en | ar | pt | es | fa | fr | UI language (fallback en) |
styles | FloorPlanEditorStyles | Optional per-component chrome overrides (M11) |
showSVGExportBtn | boolean | When true, shows the Export SVG top-bar button and modal (default false) |
Peer dependencies: react, react-dom
Single JSON blob for save/load. Top-level fields: schemaVersion, id, title, updatedAt, canvas, nodes, tiers, holds, optional selection.
import { createEmptyFloorPlan } from "@ticketss/floor-plan-editor";
const doc = createEmptyFloorPlan({ title: "Main Hall" });See docs/floor-plan-json-schema-reference.md in the repo for the full schema.
Entity examples and validation — documented in schema reference.
| Entity | Tier | Hold |
|---|---|---|
| Table | Whole table | Whole table |
| G.A. Area | Whole zone | Whole zone |
| Seated section seats | Per seat | Per seat |
| Image, Text, Shape | — | — |
Drawing tools and property panels — M2–M9.
Chrome UI uses CSS variables from Ticketss main.scss, shipped as @ticketss/floor-plan-editor/styles/tokens.css. Toggle theme with isDarkMode on the editor.
Figma implementations must map colors to var(--*) tokens, not raw hex. See docs/design-tokens.md for the mapping checklist.
Full token table — in design-tokens.md.
Pass styles to override chrome colors, backgrounds, and typography per component key. Canvas entities (tables, seats, shapes) stay in value.nodes — only editor chrome is customizable here.
<FloorPlanEditor
value={doc}
onChange={setDoc}
isDarkMode={false}
lang="en"
onSave={(saved) => {
console.log(saved);
alert("Floor plan saved");
}}
onCancel={() => alert("cancelled")}
styles={{
topBar: { backgroundColor: "#1a1a2e", color: "#eee" },
topBarTitle: { fontFamily: "Georgia, serif", fontSize: 20, fontWeight: 700 },
modeTabActive: { color: "#ff6b6b", fontWeight: 600 },
}}
/>tokens.css via data-themeeditor.cssstyles[key] (highest priority)| Key | Description |
|---|---|
root | Editor root wrapper — layout, background, typography baseline |
topBar | Top bar container |
topBarTitle | Editable floor plan title |
topBarButton | Undo, redo, zoom, cancel, close icon buttons |
topBarButtonPrimary | Save button |
leftRail | Left icon rail (desktop/tablet) |
leftRailButton | Plus / drawing tools toggle |
modeTabs | Mode tabs bar background |
modeTab | Inactive mode tab |
modeTabActive | Active mode tab |
canvas | Canvas viewport and stage area |
canvasGrid | Dot grid on canvas (merged with canvas element) |
drawingToolsSidebar | Drawing tools panel (left sidebar) |
drawingToolsTool | Drawing tool tile |
drawingToolsToolActive | Selected drawing tool tile |
propertiesSidebar | Entity properties panel (right) |
propertiesSidebarHeader | Properties sidebar title row |
propertiesSidebarLabel | Property field labels |
propertiesSidebarInput | Text inputs, textareas, custom selects |
propertiesSidebarButton | Save, delete, duplicate actions in properties |
tiersSidebar | Assign pricing tiers panel |
holdsSidebar | Seat holds panel |
editSeatSidebar | Single/multi seat edit panel |
Each key accepts: color, backgroundColor, fontFamily, fontSize, fontWeight, borderColor, plus any other valid CSS property as an escape hatch.
Rebrand top bar and active tab:
styles={{
topBar: { backgroundColor: "#1a1a2e", color: "#eee" },
modeTabActive: { color: "#ff6b6b", fontWeight: 600 },
}}White-label drawing tools sidebar:
styles={{
drawingToolsSidebar: { backgroundColor: "#fafafa" },
drawingToolsToolActive: {
backgroundColor: "#e8f4fc",
color: "#0F66A9",
fontWeight: 600,
},
}}Select a component key in the tree below, edit override fields, and watch the editor above update immediately.
Select a styles key to edit overrides in the playground. CSS classes listed under each node are for reference or global stylesheets.
Outer wrapper (`floor-plan-editor`) — theme and direction
.floor-plan-editor.fpe-chrome.fpe-body.fpe-body__main.fpe-body__workspaceTitle, undo/redo, zoom, cancel, save
.fpe-top-bar.fpe-top-bar__start.fpe-top-bar__actions.fpe-left-rail.fpe-mobile-action-bar.fpe-mobile-action-bar__plus.fpe-mode-tabs-bar.fpe-canvas-wrap.fpe-sidebar.fpe-sidebar--left.fpe-drawing-tools.fpe-sidebar.fpe-sidebar--right.fpe-sidebar.fpe-tiers-panel.fpe-sidebar.fpe-holds-panel.fpe-sidebar.fpe-edit-seat-paneltopBarTop bar container
styles JSON{}Full reference: docs/component-styles.md in the repo. Package exports: EDITOR_STYLE_REGISTRY, FLOOR_PLAN_EDITOR_STYLE_KEYS, DEFAULT_EDITOR_STYLES.
Export the floor plan as a hierarchical SVG with grouped seated sections, seats, tables, shapes, and sold overlay layers. Use the Export SVG button in the editor top bar, or call the API directly:
import {
exportFloorPlanToSvg,
downloadFloorPlanSvg,
defaultIsSeatSold,
} from "@ticketss/floor-plan-editor";
const svg = exportFloorPlanToSvg(doc, {
backgroundColor: "#F4F7FA",
soldSeatOverlaySvg: '<circle r="10" stroke="red" fill="none"/>',
allSoldOverlaySvg: '<rect width="100%" height="100%" fill="rgba(0,0,0,0.35)"/>',
isSeatSold: (seatId) => defaultIsSeatSold(seatId),
});
downloadFloorPlanSvg(doc, { isSeatSold: defaultIsSeatSold });Sold state is a placeholder in v1 (defaultIsSeatSold uses a stable hash per seat id). Pass isSeatSold from your host when real inventory is available.
Current value from the editor above:
{
"schemaVersion": 1,
"id": "fp_demo",
"title": "Floor plan editor",
"updatedAt": "2026-01-01T00:00:00.000Z",
"canvas": {
"width": 4000,
"height": 3000,
"gridSize": 20,
"zoom": 1,
"viewport": {
"x": 0,
"y": 0
}
},
"nodes": [],
"tiers": [],
"holds": [],
"selection": []
}Controlled component pattern for your app state or future API:
const [doc, setDoc] = useState(() => createEmptyFloorPlan());
<FloorPlanEditor value={doc} onChange={setDoc} isDarkMode={dark} lang="en" />
// Future: await api.putFloorPlan(eventId, doc);Top bar Save / Cancel: pass onSave (receives current FloorPlanDocument) and onCancel from your host app (e.g. persist to API or navigate away).