import { useState, type PointerEvent, type ReactNode } from "react"; import type { FurnitureType } from "../types"; const CLICK_THRESHOLD_PX = 5; interface Props { remaining: Record; onStartDrag: (type: FurnitureType, clientX: number, clientY: number) => void; onOpenMenu: (type: FurnitureType) => void; panel: ReactNode; // when set, replaces the tiles (type settings menu) } const tile: React.CSSProperties = { width: "100%", height: 84, boxSizing: "border-box", background: "#fff", border: "1px solid #cfcfcf", borderRadius: 8, display: "flex", flexDirection: "column", alignItems: "center", justifyContent: "center", gap: 6, cursor: "grab", userSelect: "none", touchAction: "none", font: "12px system-ui, sans-serif", color: "#444", }; export function ChairIcon({ size = 36 }: { size?: number }) { return ( ); } export function TableIcon({ size = 36 }: { size?: number }) { return ( ); } const MIN_W = 152; const MAX_W = 600; interface TileProps { type: FurnitureType; remaining: number; onStartDrag: Props["onStartDrag"]; onOpenMenu: Props["onOpenMenu"]; } /** Press + move > threshold = drag a new item out. Press + release without moving = open menu. */ function Tile({ type, remaining, onStartDrag, onOpenMenu }: TileProps) { const empty = remaining <= 0; const onPointerDown = (e: PointerEvent) => { e.preventDefault(); const { clientX: x0, clientY: y0 } = e; let dragging = false; const move = (ev: globalThis.PointerEvent) => { if (dragging || empty) return; if (Math.hypot(ev.clientX - x0, ev.clientY - y0) >= CLICK_THRESHOLD_PX) { dragging = true; cleanup(); onStartDrag(type, ev.clientX, ev.clientY); } }; const up = (ev: globalThis.PointerEvent) => { cleanup(); // Out-of-stock tiles can't be dragged, so any release over the tile counts as a click. if (Math.hypot(ev.clientX - x0, ev.clientY - y0) < CLICK_THRESHOLD_PX || empty) { onOpenMenu(type); } }; const cleanup = () => { window.removeEventListener("pointermove", move); window.removeEventListener("pointerup", up); }; window.addEventListener("pointermove", move); window.addEventListener("pointerup", up); }; return (
{remaining} {type === "chair" ? : } {type === "chair" ? "Chair" : "Table"}
); } export function Palette({ remaining, onStartDrag, onOpenMenu, panel }: Props) { const [width, setWidth] = useState(264); const startResize = (e: PointerEvent) => { e.preventDefault(); const startX = e.clientX; const startW = width; const move = (ev: globalThis.PointerEvent) => setWidth(Math.min(MAX_W, Math.max(MIN_W, startW + ev.clientX - startX))); const up = () => { window.removeEventListener("pointermove", move); window.removeEventListener("pointerup", up); }; window.addEventListener("pointermove", move); window.addEventListener("pointerup", up); }; return ( ); }