{"version":3,"file":"roomViewer.mjs","names":[],"sources":["../../../../package/common/roomViewer/roomViewer.tsx"],"sourcesContent":["import { useCallback, useEffect, useImperativeHandle, useId, useMemo, useRef, useState } from 'react';\nimport type { PointerEvent as ReactPointerEvent } from 'react';\nimport cx from 'classnames';\nimport { faExpand, faMagnifyingGlassMinus, faMagnifyingGlassPlus } from '@fortawesome/free-solid-svg-icons';\n\nimport { useComponentDefaults, useLabels } from '../configProvider';\nimport {\n  clientToWorld,\n  derivePlan,\n  floorBounds,\n  getFloor,\n  type PlanFloor,\n  type PlanSelection,\n  type PlanView,\n} from '../floorPlan';\nimport PlanCanvas from '../planCanvas';\nimport { renderIcon } from '../utils/renderIcon';\nimport type { RoomViewerLabels, RoomViewerProps } from './roomViewer.types';\nimport styles from './roomViewer.module.pcss';\n\nconst MIN_ZOOM = 0.002;\nconst MAX_ZOOM = 4;\n/** Past this many pixels a press is a pan rather than a click on a room. */\nconst PAN_THRESHOLD_PX = 4;\n\nfunction formatAreaDefault(areaMm2: number): string {\n  const m2 = areaMm2 / 1e6;\n  return `${m2 < 10 ? m2.toFixed(1) : Math.round(m2)} m²`;\n}\n\nconst DEFAULT_LABELS: Required<RoomViewerLabels> = {\n  floors: 'Floors',\n  zoomIn: 'Zoom in',\n  zoomOut: 'Zoom out',\n  fit: 'Fit to view',\n  empty: 'Nothing to show',\n  unnamedRoom: 'Unnamed room',\n  selectedRoom: (name, area) => `${name} selected, ${area}`,\n  noRoomSelected: 'No room selected',\n};\n\n/**\n * Read-only view of a floor plan.\n *\n * It renders the same `planCanvas` the drawer does, from the same `FloorPlan`, so the\n * two cannot drift apart. Selection and hover are React state rather than classes pushed\n * onto DOM nodes, which is what lets them be controlled from outside.\n */\nfunction RoomViewer(props: RoomViewerProps) {\n  const {\n    ref,\n    plan,\n    activeFloorId,\n    defaultActiveFloorId,\n    onFloorChange,\n    selectedRoomId,\n    defaultSelectedRoomId = null,\n    onSelect,\n    hoveredRoomId,\n    onHoverChange,\n    view,\n    defaultView,\n    onViewChange,\n    interactive = true,\n    pannable = true,\n    fitOnMount = true,\n    showFloorBar = true,\n    showViewControls = true,\n    showGrid = false,\n    showRoomLabels = true,\n    showOpeningSymbols = true,\n    getRoomColor,\n    renderRoomLabel,\n    getRoomLabel,\n    formatArea = formatAreaDefault,\n    height = 360,\n    emptyState,\n    labels,\n    icons,\n    className,\n    style,\n    classNames,\n    styles: slotStyles,\n    'aria-label': ariaLabel,\n    ...rest\n  } = useComponentDefaults('RoomViewer', props);\n  const text = useLabels('RoomViewer', labels, DEFAULT_LABELS);\n\n  const wrapperRef = useRef<HTMLDivElement>(null);\n  const svgRef = useRef<SVGSVGElement>(null);\n  const liveId = useId().replace(/:/g, '');\n\n  const floors = plan?.floors ?? [];\n\n  // ─── Floor ────────────────────────────────────────────────────────────────────\n\n  const [floorIdState, setFloorIdState] = useState<string | undefined>(defaultActiveFloorId);\n  const currentFloorId = activeFloorId ?? floorIdState ?? floors[0]?.id;\n  const floor: PlanFloor | null = useMemo(\n    () => (plan ? (getFloor(plan, currentFloorId) ?? getFloor(plan)) : null),\n    [plan, currentFloorId],\n  );\n\n  const selectFloor = useCallback(\n    (nextId: string) => {\n      if (activeFloorId === undefined) setFloorIdState(nextId);\n      onFloorChange?.(nextId);\n    },\n    [activeFloorId, onFloorChange],\n  );\n\n  const rooms = useMemo(() => (floor ? derivePlan(floor).rooms : []), [floor]);\n\n  // ─── Selection and hover ──────────────────────────────────────────────────────\n\n  const [selectedState, setSelectedState] = useState<string | null>(defaultSelectedRoomId);\n  const selected = selectedRoomId !== undefined ? selectedRoomId : selectedState;\n\n  const [hoveredState, setHoveredState] = useState<string | null>(null);\n  const hovered = hoveredRoomId !== undefined ? hoveredRoomId : hoveredState;\n\n  const selectRoom = useCallback(\n    (roomId: string | null) => {\n      if (selectedRoomId === undefined) setSelectedState(roomId);\n      const room = roomId ? (rooms.find((r) => r.roomId === roomId) ?? null) : null;\n      onSelect?.(roomId, room, floor?.id ?? '');\n    },\n    [selectedRoomId, rooms, onSelect, floor?.id],\n  );\n\n  const setHovered = useCallback(\n    (roomId: string | null) => {\n      if (hoveredRoomId === undefined) setHoveredState(roomId);\n      onHoverChange?.(roomId);\n    },\n    [hoveredRoomId, onHoverChange],\n  );\n\n  // A selection made by clicking a shape is silent to a screen reader, so it is\n  // announced separately rather than relying on focus landing somewhere useful.\n  const announcement = (() => {\n    const room = selected ? rooms.find((r) => r.roomId === selected) : undefined;\n    if (!room) return text.noRoomSelected;\n    return text.selectedRoom(room.meta?.name || text.unnamedRoom, formatArea(room.area));\n  })();\n\n  // ─── View ─────────────────────────────────────────────────────────────────────\n\n  const [viewState, setViewState] = useState<PlanView>(defaultView ?? { x: 0, y: 0, zoom: 0.05 });\n  const currentView = view ?? viewState;\n  const viewRef = useRef(currentView);\n  viewRef.current = currentView;\n\n  const applyView = useCallback(\n    (next: PlanView) => {\n      if (view === undefined) setViewState(next);\n      onViewChange?.(next);\n    },\n    [view, onViewChange],\n  );\n\n  const userMovedRef = useRef(false);\n  const [size, setSize] = useState({ width: 800, height: 360 });\n  useEffect(() => {\n    const element = wrapperRef.current;\n    if (!element) return;\n    const measure = () => {\n      const rect = element.getBoundingClientRect();\n      if (rect.width > 0 && rect.height > 0) {\n        setSize({ width: Math.round(rect.width), height: Math.round(rect.height) });\n      }\n    };\n    const observer = new ResizeObserver(measure);\n    observer.observe(element);\n    measure();\n    return () => observer.disconnect();\n  }, []);\n\n  const fitToContent = useCallback(\n    (options: { padding?: number } = {}) => {\n      const padding = options.padding ?? 24;\n      const bounds = floor ? floorBounds(floor) : null;\n      if (!bounds || bounds.width <= 0 || bounds.height <= 0) {\n        applyView({ x: -1000, y: -1000, zoom: 0.05 });\n        return;\n      }\n      const zoom = Math.max(\n        MIN_ZOOM,\n        Math.min(\n          MAX_ZOOM,\n          Math.min((size.width - padding * 2) / bounds.width, (size.height - padding * 2) / bounds.height),\n        ),\n      );\n      applyView({\n        x: bounds.minX - (size.width / zoom - bounds.width) / 2,\n        y: bounds.minY - (size.height / zoom - bounds.height) / 2,\n        zoom,\n      });\n    },\n    [floor, size.width, size.height, applyView],\n  );\n\n  /**\n   * Refit on anything that changes what \"fitted\" means — the floor, and the canvas size,\n   * which is not known until the element has been measured. The moment the viewer pans or\n   * zooms, the view is theirs and we stop touching it.\n   */\n  useEffect(() => {\n    if (!fitOnMount || userMovedRef.current) return;\n    if (!floor || size.width <= 0 || Object.keys(floor.nodes).length === 0) return;\n    fitToContent();\n  }, [fitOnMount, floor, size.width, size.height, fitToContent]);\n\n  const zoomBy = useCallback(\n    (factor: number, centre?: { x: number; y: number }) => {\n      userMovedRef.current = true;\n      const v = viewRef.current;\n      const next = Math.max(MIN_ZOOM, Math.min(MAX_ZOOM, v.zoom * factor));\n      if (next === v.zoom) return;\n      const cx0 = centre?.x ?? size.width / 2;\n      const cy0 = centre?.y ?? size.height / 2;\n      // Keep the world point under the cursor where it is.\n      applyView({\n        x: v.x + cx0 / v.zoom - cx0 / next,\n        y: v.y + cy0 / v.zoom - cy0 / next,\n        zoom: next,\n      });\n    },\n    [applyView, size.width, size.height],\n  );\n\n  // ─── Pan and pinch ────────────────────────────────────────────────────────────\n\n  const pointersRef = useRef(new Map<number, { x: number; y: number }>());\n  const panRef = useRef<{ startClient: { x: number; y: number }; startView: PlanView; moved: boolean } | null>(null);\n  const pinchRef = useRef<{ dist: number; view: PlanView; mid: { x: number; y: number } } | null>(null);\n\n  const handlePointerDown = useCallback(\n    (event: ReactPointerEvent<HTMLDivElement>) => {\n      if (!pannable) return;\n      pointersRef.current.set(event.pointerId, { x: event.clientX, y: event.clientY });\n      // Deliberately NOT capturing the pointer yet. While a capture is active the click\n      // is dispatched to the capturing element instead of the shape under the cursor, so\n      // capturing here would mean a room could never be clicked. The capture is taken in\n      // the move handler, once the press has actually turned into a pan.\n      if (pointersRef.current.size === 2) {\n        const [p1, p2] = [...pointersRef.current.values()];\n        pinchRef.current = {\n          dist: Math.hypot(p2.x - p1.x, p2.y - p1.y),\n          view: viewRef.current,\n          mid: { x: (p1.x + p2.x) / 2, y: (p1.y + p2.y) / 2 },\n        };\n        panRef.current = null;\n        return;\n      }\n      panRef.current = {\n        startClient: { x: event.clientX, y: event.clientY },\n        startView: viewRef.current,\n        moved: false,\n      };\n    },\n    [pannable],\n  );\n\n  const handlePointerMove = useCallback(\n    (event: ReactPointerEvent<HTMLDivElement>) => {\n      if (pointersRef.current.has(event.pointerId)) {\n        pointersRef.current.set(event.pointerId, { x: event.clientX, y: event.clientY });\n      }\n\n      const pinch = pinchRef.current;\n      if (pinch && pointersRef.current.size >= 2) {\n        const [p1, p2] = [...pointersRef.current.values()];\n        const now = Math.hypot(p2.x - p1.x, p2.y - p1.y);\n        if (pinch.dist <= 0) return;\n        const zoom = Math.max(MIN_ZOOM, Math.min(MAX_ZOOM, pinch.view.zoom * (now / pinch.dist)));\n        const rect = wrapperRef.current?.getBoundingClientRect() ?? { left: 0, top: 0 };\n        const mid = { x: (p1.x + p2.x) / 2, y: (p1.y + p2.y) / 2 };\n        userMovedRef.current = true;\n        const worldMid = clientToWorld(pinch.mid.x, pinch.mid.y, rect, pinch.view);\n        applyView({\n          x: worldMid.x - (mid.x - rect.left) / zoom,\n          y: worldMid.y - (mid.y - rect.top) / zoom,\n          zoom,\n        });\n        return;\n      }\n\n      const pan = panRef.current;\n      if (!pan) return;\n      const dx = event.clientX - pan.startClient.x;\n      const dy = event.clientY - pan.startClient.y;\n      if (!pan.moved && Math.hypot(dx, dy) <= PAN_THRESHOLD_PX) return;\n      if (!pan.moved) {\n        (event.currentTarget as HTMLElement).setPointerCapture?.(event.pointerId);\n      }\n      pan.moved = true;\n      userMovedRef.current = true;\n      applyView({\n        x: pan.startView.x - dx / pan.startView.zoom,\n        y: pan.startView.y - dy / pan.startView.zoom,\n        zoom: pan.startView.zoom,\n      });\n    },\n    [applyView],\n  );\n\n  const handlePointerUp = useCallback((event: ReactPointerEvent<HTMLDivElement>) => {\n    pointersRef.current.delete(event.pointerId);\n    if (pointersRef.current.size < 2) pinchRef.current = null;\n    panRef.current = null;\n  }, []);\n\n  // React's onWheel is passive, so it cannot preventDefault and the page scrolls instead.\n  useEffect(() => {\n    const element = wrapperRef.current;\n    if (!element || !pannable) return;\n    const onWheel = (event: WheelEvent) => {\n      event.preventDefault();\n      const rect = element.getBoundingClientRect();\n      zoomBy(event.deltaY < 0 ? 1.1 : 1 / 1.1, {\n        x: event.clientX - rect.left,\n        y: event.clientY - rect.top,\n      });\n    };\n    element.addEventListener('wheel', onWheel, { passive: false });\n    return () => element.removeEventListener('wheel', onWheel);\n  }, [pannable, zoomBy]);\n\n  // ─── Handle ───────────────────────────────────────────────────────────────────\n\n  useImperativeHandle(\n    ref,\n    () => ({\n      fitToContent,\n      getView: () => viewRef.current,\n      setView: applyView,\n      zoomBy: (factor: number) => zoomBy(factor),\n      select: selectRoom,\n      getFloorId: () => floor?.id ?? null,\n      setFloorId: selectFloor,\n      getSvgElement: () => svgRef.current,\n    }),\n    [fitToContent, applyView, zoomBy, selectRoom, floor?.id, selectFloor],\n  );\n\n  // ─── Render ───────────────────────────────────────────────────────────────────\n\n  const selection: readonly PlanSelection[] = useMemo(\n    () => (selected ? [{ type: 'room', id: selected } as PlanSelection] : []),\n    [selected],\n  );\n  const hoveredSelection: PlanSelection | null = useMemo(\n    () => (hovered ? { type: 'room', id: hovered } : null),\n    [hovered],\n  );\n\n  const isEmpty = !floor || Object.keys(floor.walls).length === 0;\n\n  return (\n    <div\n      {...rest}\n      className={cx(styles.roomViewer, className, classNames?.root)}\n      style={{ ...style, ...slotStyles?.root }}\n      aria-label={ariaLabel ?? 'Floor plan'}\n    >\n      {showFloorBar && floors.length > 1 && (\n        <div\n          className={cx(styles.roomViewerFloorBar, classNames?.floorBar)}\n          style={slotStyles?.floorBar}\n          role='tablist'\n          aria-label={text.floors}\n        >\n          {/* Top storey first, the way a building reads in section. */}\n          {[...floors].reverse().map((f) => (\n            <button\n              key={f.id}\n              type='button'\n              role='tab'\n              className={cx(\n                styles.roomViewerFloorTab,\n                f.id === floor?.id && styles.roomViewerFloorTabActive,\n                classNames?.floorTab,\n              )}\n              style={slotStyles?.floorTab}\n              aria-selected={f.id === floor?.id}\n              onClick={() => selectFloor(f.id)}\n            >\n              {f.label}\n            </button>\n          ))}\n        </div>\n      )}\n\n      <div\n        ref={wrapperRef}\n        className={cx(\n          styles.roomViewerCanvasWrapper,\n          pannable && styles.roomViewerCanvasWrapperPannable,\n          classNames?.canvasWrapper,\n        )}\n        style={{ height, ...slotStyles?.canvasWrapper }}\n        // Any button pans, the middle one included. Left alone, the browser starts its autoscroll\n        // on a middle press and scrolls the page away underneath the pan.\n        onMouseDown={(e) => {\n          if (pannable && e.button === 1) e.preventDefault();\n        }}\n        onPointerDown={handlePointerDown}\n        onPointerMove={handlePointerMove}\n        onPointerUp={handlePointerUp}\n        onPointerCancel={handlePointerUp}\n      >\n        {isEmpty ? (\n          <div className={cx(styles.roomViewerEmpty, classNames?.empty)} style={slotStyles?.empty}>\n            {emptyState ?? text.empty}\n          </div>\n        ) : (\n          <PlanCanvas\n            ref={svgRef}\n            floor={floor}\n            rooms={rooms}\n            view={currentView}\n            size={size}\n            selection={selection}\n            hovered={hoveredSelection}\n            interactiveRooms={interactive}\n            showGrid={showGrid}\n            showRoomLabels={showRoomLabels}\n            showOpeningSymbols={showOpeningSymbols}\n            getRoomColor={getRoomColor}\n            renderRoomLabel={renderRoomLabel}\n            getRoomLabel={\n              getRoomLabel ?? ((room, { area }) => `${room?.name || text.unnamedRoom}, ${formatArea(area)}`)\n            }\n            formatArea={formatArea}\n            onRoomActivate={interactive ? (roomId) => selectRoom(roomId) : undefined}\n            onBackgroundActivate={interactive ? () => selectRoom(null) : undefined}\n            onHoverChange={interactive ? (target) => setHovered(target?.type === 'room' ? target.id : null) : undefined}\n            classNames={{\n              canvas: classNames?.canvas,\n              grid: classNames?.grid,\n              rooms: classNames?.rooms,\n              roomFill: classNames?.roomFill,\n              roomLabel: classNames?.roomLabel,\n              walls: classNames?.walls,\n              wallBody: classNames?.wallBody,\n              openings: classNames?.openings,\n              opening: classNames?.opening,\n            }}\n            styles={{\n              canvas: slotStyles?.canvas,\n              grid: slotStyles?.grid,\n              rooms: slotStyles?.rooms,\n              roomFill: slotStyles?.roomFill,\n              roomLabel: slotStyles?.roomLabel,\n              walls: slotStyles?.walls,\n              wallBody: slotStyles?.wallBody,\n              openings: slotStyles?.openings,\n              opening: slotStyles?.opening,\n            }}\n            aria-label={ariaLabel ?? 'Floor plan'}\n          />\n        )}\n\n        {showViewControls && !isEmpty && (\n          <div className={cx(styles.roomViewerViewControls, classNames?.viewControls)} style={slotStyles?.viewControls}>\n            <button\n              type='button'\n              className={cx(styles.roomViewerViewButton, classNames?.viewButton)}\n              style={slotStyles?.viewButton}\n              title={text.zoomOut}\n              aria-label={text.zoomOut}\n              onClick={() => zoomBy(1 / 1.2)}\n            >\n              {renderIcon(icons?.zoomOut ?? faMagnifyingGlassMinus)}\n            </button>\n            <button\n              type='button'\n              className={cx(styles.roomViewerViewButton, classNames?.viewButton)}\n              style={slotStyles?.viewButton}\n              title={text.fit}\n              aria-label={text.fit}\n              onClick={() => {\n                userMovedRef.current = false;\n                fitToContent();\n              }}\n            >\n              {renderIcon(icons?.fit ?? faExpand)}\n            </button>\n            <button\n              type='button'\n              className={cx(styles.roomViewerViewButton, classNames?.viewButton)}\n              style={slotStyles?.viewButton}\n              title={text.zoomIn}\n              aria-label={text.zoomIn}\n              onClick={() => zoomBy(1.2)}\n            >\n              {renderIcon(icons?.zoomIn ?? faMagnifyingGlassPlus)}\n            </button>\n          </div>\n        )}\n      </div>\n\n      <div id={liveId} className={styles.roomViewerLive} role='status' aria-live='polite'>\n        {announcement}\n      </div>\n    </div>\n  );\n}\n\nexport default RoomViewer;\n"],"mappings":"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"}