@spacesync/client 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (100) hide show
  1. package/README.md +39 -0
  2. package/dist/analog-clock-picker.d.ts +10 -0
  3. package/dist/analog-clock-picker.js +408 -0
  4. package/dist/analog-clock-picker.js.map +1 -0
  5. package/dist/api.d.ts +222 -0
  6. package/dist/api.js +167 -0
  7. package/dist/api.js.map +1 -0
  8. package/dist/app-modal.d.ts +25 -0
  9. package/dist/app-modal.js +145 -0
  10. package/dist/app-modal.js.map +1 -0
  11. package/dist/app-shell-mobile-nav.d.ts +29 -0
  12. package/dist/app-shell-mobile-nav.js +122 -0
  13. package/dist/app-shell-mobile-nav.js.map +1 -0
  14. package/dist/app-web-shell.d.ts +20 -0
  15. package/dist/app-web-shell.js +45 -0
  16. package/dist/app-web-shell.js.map +1 -0
  17. package/dist/booking-schedule-calendars.d.ts +95 -0
  18. package/dist/booking-schedule-calendars.js +1343 -0
  19. package/dist/booking-schedule-calendars.js.map +1 -0
  20. package/dist/chat-stick-to-bottom.d.ts +22 -0
  21. package/dist/chat-stick-to-bottom.js +190 -0
  22. package/dist/chat-stick-to-bottom.js.map +1 -0
  23. package/dist/detail-page-header.d.ts +16 -0
  24. package/dist/detail-page-header.js +220 -0
  25. package/dist/detail-page-header.js.map +1 -0
  26. package/dist/dialog-CIdvylPK.d.ts +16 -0
  27. package/dist/errors-D1niseaa.d.ts +34 -0
  28. package/dist/file-picker-field.d.ts +23 -0
  29. package/dist/file-picker-field.js +165 -0
  30. package/dist/file-picker-field.js.map +1 -0
  31. package/dist/file-viewer-dialog.d.ts +15 -0
  32. package/dist/file-viewer-dialog.js +387 -0
  33. package/dist/file-viewer-dialog.js.map +1 -0
  34. package/dist/floor-switcher.d.ts +14 -0
  35. package/dist/floor-switcher.js +416 -0
  36. package/dist/floor-switcher.js.map +1 -0
  37. package/dist/forms.d.ts +52 -0
  38. package/dist/forms.js +66 -0
  39. package/dist/forms.js.map +1 -0
  40. package/dist/image-picker-field.d.ts +11 -0
  41. package/dist/image-picker-field.js +135 -0
  42. package/dist/image-picker-field.js.map +1 -0
  43. package/dist/layout-canvas-view.d.ts +57 -0
  44. package/dist/layout-canvas-view.js +974 -0
  45. package/dist/layout-canvas-view.js.map +1 -0
  46. package/dist/lib/dateDisplay.d.ts +24 -0
  47. package/dist/lib/dateDisplay.js +84 -0
  48. package/dist/lib/dateDisplay.js.map +1 -0
  49. package/dist/live-resource-meta.d.ts +11 -0
  50. package/dist/live-resource-meta.js +27 -0
  51. package/dist/live-resource-meta.js.map +1 -0
  52. package/dist/live-space-pulse-dot.d.ts +8 -0
  53. package/dist/live-space-pulse-dot.js +18 -0
  54. package/dist/live-space-pulse-dot.js.map +1 -0
  55. package/dist/notification-sidebar-preview.d.ts +37 -0
  56. package/dist/notification-sidebar-preview.js +121 -0
  57. package/dist/notification-sidebar-preview.js.map +1 -0
  58. package/dist/page-list-chrome.d.ts +124 -0
  59. package/dist/page-list-chrome.js +970 -0
  60. package/dist/page-list-chrome.js.map +1 -0
  61. package/dist/platform-support-message-composer.d.ts +38 -0
  62. package/dist/platform-support-message-composer.js +446 -0
  63. package/dist/platform-support-message-composer.js.map +1 -0
  64. package/dist/shell-sidebar-nav.d.ts +16 -0
  65. package/dist/shell-sidebar-nav.js +69 -0
  66. package/dist/shell-sidebar-nav.js.map +1 -0
  67. package/dist/shell-theme.d.ts +6 -0
  68. package/dist/shell-theme.js +9 -0
  69. package/dist/shell-theme.js.map +1 -0
  70. package/dist/shell-top-bar-chrome.d.ts +39 -0
  71. package/dist/shell-top-bar-chrome.js +105 -0
  72. package/dist/shell-top-bar-chrome.js.map +1 -0
  73. package/dist/shell-top-bar.d.ts +16 -0
  74. package/dist/shell-top-bar.js +71 -0
  75. package/dist/shell-top-bar.js.map +1 -0
  76. package/dist/styles/theme.css +223 -0
  77. package/dist/styles/theme.css.map +1 -0
  78. package/dist/styles/theme.d.ts +2 -0
  79. package/dist/support-presence-status.d.ts +11 -0
  80. package/dist/support-presence-status.js +92 -0
  81. package/dist/support-presence-status.js.map +1 -0
  82. package/dist/theme-appearance-control.d.ts +5 -0
  83. package/dist/theme-appearance-control.js +114 -0
  84. package/dist/theme-appearance-control.js.map +1 -0
  85. package/dist/theme-boot-script.d.ts +4 -0
  86. package/dist/theme-boot-script.js +9 -0
  87. package/dist/theme-boot-script.js.map +1 -0
  88. package/dist/theme-provider.d.ts +9 -0
  89. package/dist/theme-provider.js +25 -0
  90. package/dist/theme-provider.js.map +1 -0
  91. package/dist/ui.d.ts +765 -0
  92. package/dist/ui.js +5637 -0
  93. package/dist/ui.js.map +1 -0
  94. package/dist/use-client-pagination.d.ts +22 -0
  95. package/dist/use-client-pagination.js +51 -0
  96. package/dist/use-client-pagination.js.map +1 -0
  97. package/dist/use-client-sort.d.ts +20 -0
  98. package/dist/use-client-sort.js +41 -0
  99. package/dist/use-client-sort.js.map +1 -0
  100. package/package.json +208 -0
@@ -0,0 +1,974 @@
1
+ import * as React from 'react';
2
+ import { useMemo, useState, useRef, useCallback, useEffect } from 'react';
3
+ import { Minus, Plus, Maximize2, Box, DoorOpen, Armchair, Search, Calendar } from 'lucide-react';
4
+ import { clsx } from 'clsx';
5
+ import { twMerge } from 'tailwind-merge';
6
+ import { Slot } from '@radix-ui/react-slot';
7
+ import { cva } from 'class-variance-authority';
8
+ import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
9
+ import * as PopoverPrimitive from '@radix-ui/react-popover';
10
+ import * as TooltipPrimitive from '@radix-ui/react-tooltip';
11
+ import { useTheme } from 'next-themes';
12
+
13
+ // src/live-space/layout-canvas-view.tsx
14
+
15
+ // src/live-space/layout-canvas-types.ts
16
+ var DEFAULT_CANVAS_WIDTH = 1200;
17
+ var DEFAULT_CANVAS_HEIGHT = 900;
18
+ var LIVE_RESOURCE_TYPE_META = {
19
+ desks: {
20
+ label: "Desk",
21
+ rowAccent: "text-blue-700 bg-blue-50 border-blue-100",
22
+ Icon: Armchair,
23
+ iconTile: "bg-blue-50 text-blue-600 border-blue-100 dark:bg-blue-950/50 dark:text-blue-400 dark:border-blue-900/50"
24
+ },
25
+ rooms: {
26
+ label: "Room",
27
+ rowAccent: "text-orange-700 bg-orange-50 border-orange-100",
28
+ Icon: DoorOpen,
29
+ iconTile: "bg-orange-50 text-orange-600 border-orange-100 dark:bg-orange-950/50 dark:text-orange-400 dark:border-orange-900/50"
30
+ },
31
+ other: {
32
+ label: "Other",
33
+ rowAccent: "text-purple-700 bg-purple-50 border-purple-100",
34
+ Icon: Box,
35
+ iconTile: "bg-purple-50 text-purple-600 border-purple-100 dark:bg-purple-950/50 dark:text-purple-400 dark:border-purple-900/50"
36
+ }
37
+ };
38
+ function cn(...inputs) {
39
+ return twMerge(clsx(inputs));
40
+ }
41
+ var buttonVariants = cva(
42
+ "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium cursor-pointer transition-all disabled:pointer-events-none disabled:opacity-50 disabled:cursor-not-allowed [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",
43
+ {
44
+ variants: {
45
+ variant: {
46
+ default: "bg-primary text-primary-foreground hover:bg-primary/90 active:bg-primary/80 active:scale-[0.98]",
47
+ destructive: "bg-destructive text-white hover:bg-destructive/90 active:bg-destructive/80 active:scale-[0.98] focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60 dark:active:bg-destructive/70",
48
+ outline: "border bg-background text-foreground hover:bg-accent hover:text-accent-foreground active:bg-accent/80 active:scale-[0.98] dark:bg-input/30 dark:border-input dark:hover:bg-input/50 dark:active:bg-input/60",
49
+ secondary: "bg-secondary text-secondary-foreground hover:bg-secondary/80 active:bg-secondary/70 active:scale-[0.98]",
50
+ ghost: "hover:bg-accent hover:text-accent-foreground active:bg-accent/90 active:scale-[0.98] dark:hover:bg-accent/50 dark:active:bg-accent/40",
51
+ link: "text-primary underline-offset-4 hover:underline active:text-primary/80"
52
+ },
53
+ size: {
54
+ default: "h-9 px-4 py-2 has-[>svg]:px-3",
55
+ sm: "h-8 rounded-md gap-1.5 px-3 has-[>svg]:px-2.5",
56
+ lg: "h-10 rounded-md px-6 has-[>svg]:px-4",
57
+ icon: "size-9 rounded-md"
58
+ }
59
+ },
60
+ defaultVariants: {
61
+ variant: "default",
62
+ size: "default"
63
+ }
64
+ }
65
+ );
66
+ var Button = React.forwardRef(function Button2({ className, variant, size, asChild = false, ...props }, ref) {
67
+ const Comp = asChild ? Slot : "button";
68
+ return /* @__PURE__ */ jsx(
69
+ Comp,
70
+ {
71
+ ref,
72
+ "data-slot": "button",
73
+ className: cn(buttonVariants({ variant, size }), className),
74
+ ...props
75
+ }
76
+ );
77
+ });
78
+ Button.displayName = "Button";
79
+ var inputVariants = cva(
80
+ "file:text-foreground placeholder:text-muted-foreground selection:bg-primary selection:text-primary-foreground border-border bg-card text-foreground flex w-full min-w-0 rounded-md border px-3 py-1 text-base transition-[color,box-shadow] outline-none file:inline-flex file:h-7 file:border-0 file:bg-transparent file:text-sm file:font-medium focus-visible:border-blue-400 focus-visible:ring-[3px] focus-visible:ring-blue-500/25 disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 aria-invalid:border-red-400 aria-invalid:ring-red-500/20 md:text-sm",
81
+ {
82
+ variants: {
83
+ size: {
84
+ sm: "h-8 text-sm",
85
+ default: "h-9",
86
+ lg: "h-10",
87
+ xl: "h-11"
88
+ }
89
+ },
90
+ defaultVariants: {
91
+ size: "default"
92
+ }
93
+ }
94
+ );
95
+ var Input = React.forwardRef(function Input2({ className, type, size, ...props }, ref) {
96
+ return /* @__PURE__ */ jsx(
97
+ "input",
98
+ {
99
+ ref,
100
+ type,
101
+ "data-slot": "input",
102
+ className: cn(inputVariants({ size }), className),
103
+ ...props
104
+ }
105
+ );
106
+ });
107
+ function SearchField({
108
+ value,
109
+ onChange,
110
+ placeholder,
111
+ className,
112
+ size = "default",
113
+ "aria-label": ariaLabel
114
+ }) {
115
+ const heightClass = size === "lg" ? "h-11" : "h-10";
116
+ const iconSize = size === "lg" ? 16 : 16;
117
+ return /* @__PURE__ */ jsxs(
118
+ "div",
119
+ {
120
+ className: cn(
121
+ "flex items-center gap-2 rounded-md border border-border bg-card px-3 w-full focus-within:border-blue-400 focus-within:ring-[3px] focus-within:ring-blue-500/25 transition-all",
122
+ heightClass,
123
+ className
124
+ ),
125
+ children: [
126
+ /* @__PURE__ */ jsx(Search, { size: iconSize, className: "text-muted-foreground shrink-0", strokeWidth: 2.25 }),
127
+ /* @__PURE__ */ jsx(
128
+ Input,
129
+ {
130
+ type: "search",
131
+ value,
132
+ onChange: (e) => onChange(e.target.value),
133
+ placeholder,
134
+ "aria-label": ariaLabel,
135
+ className: "flex-1 border-0 bg-transparent shadow-none focus-visible:ring-0 px-0 min-w-0 h-auto"
136
+ }
137
+ )
138
+ ]
139
+ }
140
+ );
141
+ }
142
+ function PageListSearchField({
143
+ value,
144
+ onChange,
145
+ placeholder,
146
+ className
147
+ }) {
148
+ return /* @__PURE__ */ jsx(
149
+ SearchField,
150
+ {
151
+ value,
152
+ onChange,
153
+ placeholder,
154
+ className
155
+ }
156
+ );
157
+ }
158
+ function Popover({ ...props }) {
159
+ return /* @__PURE__ */ jsx(PopoverPrimitive.Root, { "data-slot": "popover", ...props });
160
+ }
161
+ function PopoverContent({
162
+ className,
163
+ align = "center",
164
+ sideOffset = 4,
165
+ ...props
166
+ }) {
167
+ return /* @__PURE__ */ jsx(PopoverPrimitive.Portal, { children: /* @__PURE__ */ jsx(
168
+ PopoverPrimitive.Content,
169
+ {
170
+ "data-slot": "popover-content",
171
+ align,
172
+ sideOffset,
173
+ className: cn(
174
+ "bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 w-72 origin-(--radix-popover-content-transform-origin) rounded-md border border-white/10 p-4 shadow-md outline-hidden",
175
+ className
176
+ ),
177
+ ...props
178
+ }
179
+ ) });
180
+ }
181
+ function PopoverAnchor({ ...props }) {
182
+ return /* @__PURE__ */ jsx(PopoverPrimitive.Anchor, { "data-slot": "popover-anchor", ...props });
183
+ }
184
+ function TooltipProvider({
185
+ delayDuration = 0,
186
+ ...props
187
+ }) {
188
+ return /* @__PURE__ */ jsx(
189
+ TooltipPrimitive.Provider,
190
+ {
191
+ "data-slot": "tooltip-provider",
192
+ delayDuration,
193
+ ...props
194
+ }
195
+ );
196
+ }
197
+ function Tooltip({ ...props }) {
198
+ return /* @__PURE__ */ jsx(TooltipProvider, { children: /* @__PURE__ */ jsx(TooltipPrimitive.Root, { "data-slot": "tooltip", ...props }) });
199
+ }
200
+ function TooltipTrigger({ ...props }) {
201
+ return /* @__PURE__ */ jsx(TooltipPrimitive.Trigger, { "data-slot": "tooltip-trigger", ...props });
202
+ }
203
+ function TooltipContent({
204
+ className,
205
+ sideOffset = 0,
206
+ children,
207
+ ...props
208
+ }) {
209
+ return /* @__PURE__ */ jsx(TooltipPrimitive.Portal, { children: /* @__PURE__ */ jsxs(
210
+ TooltipPrimitive.Content,
211
+ {
212
+ "data-slot": "tooltip-content",
213
+ sideOffset,
214
+ className: cn(
215
+ "bg-primary text-primary-foreground animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 w-fit origin-(--radix-tooltip-content-transform-origin) rounded-md px-3 py-1.5 text-xs text-balance",
216
+ className
217
+ ),
218
+ ...props,
219
+ children: [
220
+ children,
221
+ /* @__PURE__ */ jsx(TooltipPrimitive.Arrow, { className: "bg-primary fill-primary z-50 size-2.5 translate-y-[calc(-50%_-_2px)] rotate-45 rounded-[2px]" })
222
+ ]
223
+ }
224
+ ) });
225
+ }
226
+ function matchesCanvasResourceSearch(resourceName, planName, query) {
227
+ const q = query.trim().toLowerCase();
228
+ if (!q) return true;
229
+ const name = resourceName.toLowerCase();
230
+ const plan = planName?.toLowerCase() ?? "";
231
+ return name.includes(q) || plan.includes(q);
232
+ }
233
+ function resourceMatchesSearch(row, query) {
234
+ return matchesCanvasResourceSearch(row.resource.name, row.plan?.name, query);
235
+ }
236
+ var CLICK_THRESHOLD_PX = 5;
237
+ var POPOVER_CLOSE_DELAY_MS = 250;
238
+ var CANVAS_QUICK_BOOK_POPOVER_CLASS = "z-[100] w-56 border-border bg-card p-3 text-card-foreground shadow-lg";
239
+ function usePrefersHover() {
240
+ const [prefersHover, setPrefersHover] = useState(
241
+ () => typeof window !== "undefined" && window.matchMedia("(hover: hover)").matches
242
+ );
243
+ useEffect(() => {
244
+ const mql = window.matchMedia("(hover: hover)");
245
+ const onChange = () => setPrefersHover(mql.matches);
246
+ mql.addEventListener("change", onChange);
247
+ return () => mql.removeEventListener("change", onChange);
248
+ }, []);
249
+ return prefersHover;
250
+ }
251
+ function isCanvasResourceTarget(target) {
252
+ return target instanceof HTMLElement && target.closest("[data-canvas-resource]") != null;
253
+ }
254
+ function ResourceQuickBookPopoverContent({
255
+ row,
256
+ onBook,
257
+ getBookingHref
258
+ }) {
259
+ const { resource, merged } = row;
260
+ const meta = LIVE_RESOURCE_TYPE_META[resource.resourceType];
261
+ const TypeIcon = meta.Icon;
262
+ const bookingHref = getBookingHref?.(resource.id);
263
+ const handleBookPointerDown = (e) => {
264
+ e.preventDefault();
265
+ e.stopPropagation();
266
+ onBook?.(resource.id);
267
+ };
268
+ const bookLabel = /* @__PURE__ */ jsxs(Fragment, { children: [
269
+ /* @__PURE__ */ jsx(Calendar, { size: 14 }),
270
+ "Book"
271
+ ] });
272
+ return /* @__PURE__ */ jsxs("div", { className: "space-y-3", children: [
273
+ /* @__PURE__ */ jsxs("div", { className: "flex min-w-0 items-start gap-2.5", children: [
274
+ /* @__PURE__ */ jsx(
275
+ "span",
276
+ {
277
+ className: cn(
278
+ "flex h-8 w-8 shrink-0 items-center justify-center rounded-lg border",
279
+ meta.iconTile
280
+ ),
281
+ "aria-hidden": true,
282
+ children: /* @__PURE__ */ jsx(TypeIcon, { size: 16, strokeWidth: 1.75 })
283
+ }
284
+ ),
285
+ /* @__PURE__ */ jsxs("div", { className: "min-w-0 flex-1", children: [
286
+ /* @__PURE__ */ jsx("p", { className: "truncate text-sm font-semibold leading-snug text-card-foreground", children: resource.name }),
287
+ /* @__PURE__ */ jsxs(
288
+ "span",
289
+ {
290
+ className: cn(
291
+ "mt-1.5 inline-flex items-center gap-1 rounded-full border px-2 py-0.5 text-[11px] font-medium",
292
+ merged.occupied ? "border-red-100 bg-red-50 text-red-700 dark:border-red-900/50 dark:bg-red-950/50 dark:text-red-300" : "border-emerald-100 bg-emerald-50 text-emerald-800 dark:border-emerald-900/50 dark:bg-emerald-950/50 dark:text-emerald-300"
293
+ ),
294
+ children: [
295
+ /* @__PURE__ */ jsx(
296
+ "span",
297
+ {
298
+ className: cn(
299
+ "h-1.5 w-1.5 shrink-0 rounded-full",
300
+ merged.occupied ? "bg-red-500" : "bg-emerald-500"
301
+ )
302
+ }
303
+ ),
304
+ merged.occupied ? "Occupied" : "Free"
305
+ ]
306
+ }
307
+ ),
308
+ merged.occupied && merged.expectedFreeLabel && merged.expectedFreeLabel !== "\u2014" ? /* @__PURE__ */ jsx("p", { className: "mt-1 text-xs text-muted-foreground", children: merged.expectedFreeLabel }) : null
309
+ ] })
310
+ ] }),
311
+ onBook ? bookingHref ? /* @__PURE__ */ jsx(Button, { asChild: true, size: "sm", className: "w-full", children: /* @__PURE__ */ jsx("a", { href: bookingHref, onPointerDown: handleBookPointerDown, children: bookLabel }) }) : /* @__PURE__ */ jsx(
312
+ Button,
313
+ {
314
+ type: "button",
315
+ size: "sm",
316
+ className: "w-full",
317
+ onPointerDown: handleBookPointerDown,
318
+ children: bookLabel
319
+ }
320
+ ) : /* @__PURE__ */ jsx("p", { className: "text-xs leading-relaxed text-muted-foreground", children: "Booking is not available for your account." })
321
+ ] });
322
+ }
323
+ var MIN_ZOOM = 0.12;
324
+ var MAX_ZOOM = 4;
325
+ var MIN_CARD_W = 90;
326
+ var MIN_CARD_H = 56;
327
+ var CANVAS_GRID_SIZE = 40;
328
+ function canvasGridBackground(isDark) {
329
+ const line = isDark ? "oklch(0.35 0 0)" : "#e5e7eb";
330
+ return `linear-gradient(to right, ${line} 1px, transparent 1px),linear-gradient(to bottom, ${line} 1px, transparent 1px)`;
331
+ }
332
+ var CANVAS_GRID_BG_SIZE = `${CANVAS_GRID_SIZE}px ${CANVAS_GRID_SIZE}px`;
333
+ var TYPE_META_CARD = {
334
+ desks: {
335
+ border: "border-amber-200",
336
+ bg: "bg-card",
337
+ iconBg: "bg-amber-100",
338
+ iconColor: "text-amber-600",
339
+ badgeBg: "bg-amber-50",
340
+ badgeText: "text-amber-700"
341
+ },
342
+ rooms: {
343
+ border: "border-blue-200",
344
+ bg: "bg-card",
345
+ iconBg: "bg-blue-100",
346
+ iconColor: "text-blue-600 dark:text-blue-400",
347
+ badgeBg: "bg-blue-50",
348
+ badgeText: "text-blue-700 dark:text-blue-400"
349
+ },
350
+ other: {
351
+ border: "border-slate-200",
352
+ bg: "bg-card",
353
+ iconBg: "bg-slate-100",
354
+ iconColor: "text-slate-600",
355
+ badgeBg: "bg-slate-50",
356
+ badgeText: "text-slate-600"
357
+ }
358
+ };
359
+ var TYPE_ICON = {
360
+ desks: Armchair,
361
+ rooms: DoorOpen,
362
+ other: Box
363
+ };
364
+ function isFloorResourceType(type) {
365
+ return type === "desks" || type === "rooms";
366
+ }
367
+ function poolSlotsLabel(totalSlots) {
368
+ if (totalSlots == null) return null;
369
+ return `${totalSlots} slot${totalSlots !== 1 ? "s" : ""}`;
370
+ }
371
+ function PoolResourceChip({
372
+ row,
373
+ onClick,
374
+ quickBookConfig,
375
+ searchQuery,
376
+ isMatch
377
+ }) {
378
+ const { resource, merged } = row;
379
+ const m = TYPE_META_CARD.other;
380
+ const slots = poolSlotsLabel(resource.totalSlots);
381
+ const searchActive = searchQuery.trim().length > 0;
382
+ const chip = /* @__PURE__ */ jsxs(
383
+ "button",
384
+ {
385
+ type: "button",
386
+ "data-canvas-resource": "",
387
+ onClick: (e) => {
388
+ e.stopPropagation();
389
+ if (quickBookConfig) {
390
+ if (!quickBookConfig.shouldIgnoreToggle()) quickBookConfig.onToggle();
391
+ } else onClick?.();
392
+ },
393
+ onMouseEnter: () => {
394
+ if (quickBookConfig?.hover.prefersHover) {
395
+ quickBookConfig.hover.clearClose();
396
+ quickBookConfig.onOpen();
397
+ }
398
+ },
399
+ onMouseLeave: () => {
400
+ if (quickBookConfig?.hover.prefersHover) quickBookConfig.hover.scheduleClose();
401
+ },
402
+ className: cn(
403
+ "inline-flex items-center gap-2 shrink-0 px-3 py-1.5 rounded-lg border bg-card hover:bg-accent transition-colors text-left",
404
+ m.border,
405
+ searchActive && !isMatch && "opacity-40 saturate-50"
406
+ ),
407
+ children: [
408
+ /* @__PURE__ */ jsx(
409
+ "div",
410
+ {
411
+ className: cn(
412
+ "w-6 h-6 rounded-md flex items-center justify-center shrink-0",
413
+ m.iconBg,
414
+ m.iconColor
415
+ ),
416
+ children: /* @__PURE__ */ jsx(Box, { size: 12 })
417
+ }
418
+ ),
419
+ /* @__PURE__ */ jsx("span", { className: "text-xs font-medium text-foreground whitespace-nowrap", children: resource.name }),
420
+ /* @__PURE__ */ jsx(
421
+ "span",
422
+ {
423
+ className: cn(
424
+ "h-2 w-2 rounded-full shrink-0",
425
+ merged.occupied ? "bg-red-500" : "bg-emerald-500"
426
+ )
427
+ }
428
+ ),
429
+ slots && /* @__PURE__ */ jsx("span", { className: "text-[10px] text-muted-foreground whitespace-nowrap", children: slots })
430
+ ]
431
+ }
432
+ );
433
+ if (!quickBookConfig) return chip;
434
+ return /* @__PURE__ */ jsxs(
435
+ Popover,
436
+ {
437
+ modal: false,
438
+ open: quickBookConfig.open,
439
+ onOpenChange: quickBookConfig.onOpenChange,
440
+ children: [
441
+ /* @__PURE__ */ jsx(PopoverAnchor, { asChild: true, children: chip }),
442
+ /* @__PURE__ */ jsx(
443
+ PopoverContent,
444
+ {
445
+ side: "top",
446
+ align: "center",
447
+ sideOffset: 6,
448
+ className: CANVAS_QUICK_BOOK_POPOVER_CLASS,
449
+ onOpenAutoFocus: (e) => e.preventDefault(),
450
+ onMouseEnter: quickBookConfig.hover.clearClose,
451
+ onMouseLeave: quickBookConfig.hover.scheduleClose,
452
+ children: /* @__PURE__ */ jsx(
453
+ ResourceQuickBookPopoverContent,
454
+ {
455
+ row,
456
+ onBook: quickBookConfig.quickBook.onBook,
457
+ getBookingHref: quickBookConfig.quickBook.getBookingHref
458
+ }
459
+ )
460
+ }
461
+ )
462
+ ]
463
+ }
464
+ );
465
+ }
466
+ function PlacedCardView({
467
+ id,
468
+ rect,
469
+ row,
470
+ onSelect,
471
+ quickBookConfig,
472
+ searchQuery,
473
+ isMatch
474
+ }) {
475
+ const { resource, merged, plan } = row;
476
+ const type = resource.resourceType;
477
+ const m = TYPE_META_CARD[type];
478
+ const TypeIcon = TYPE_ICON[type];
479
+ const w = Math.max(rect.w, MIN_CARD_W);
480
+ const h = Math.max(rect.h, MIN_CARD_H);
481
+ const statusLabel = merged.occupied ? "Occupied" : merged.manual ? "Vacant" : "Free";
482
+ const pointerStart = useRef(null);
483
+ const searchActive = searchQuery.trim().length > 0;
484
+ const handlePointerDown = (e) => {
485
+ if (e.button !== 0) return;
486
+ e.stopPropagation();
487
+ pointerStart.current = { x: e.clientX, y: e.clientY };
488
+ };
489
+ const handlePointerUp = (e) => {
490
+ e.stopPropagation();
491
+ const start = pointerStart.current;
492
+ pointerStart.current = null;
493
+ if (!start || quickBookConfig) return;
494
+ if (Math.hypot(e.clientX - start.x, e.clientY - start.y) < CLICK_THRESHOLD_PX) {
495
+ onSelect?.(id);
496
+ }
497
+ };
498
+ const statusDot = /* @__PURE__ */ jsx(
499
+ "span",
500
+ {
501
+ className: cn(
502
+ "block h-2.5 w-2.5 rounded-full ring-2 ring-white shadow-sm",
503
+ merged.occupied ? "bg-red-500 animate-pulse" : "bg-emerald-500"
504
+ ),
505
+ "aria-label": statusLabel
506
+ }
507
+ );
508
+ const card = /* @__PURE__ */ jsxs(
509
+ "button",
510
+ {
511
+ type: "button",
512
+ "data-canvas-resource": "",
513
+ style: {
514
+ position: "absolute",
515
+ left: rect.x,
516
+ top: rect.y,
517
+ width: w,
518
+ height: h,
519
+ touchAction: "manipulation"
520
+ },
521
+ className: cn(
522
+ "rounded-xl border shadow-sm overflow-hidden select-none text-left cursor-pointer hover:shadow-md transition-shadow focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-blue-500/40",
523
+ m.border,
524
+ m.bg,
525
+ searchActive && !isMatch && "opacity-40 saturate-50"
526
+ ),
527
+ onPointerDown: handlePointerDown,
528
+ onPointerUp: handlePointerUp,
529
+ onPointerCancel: handlePointerUp,
530
+ onClick: (e) => {
531
+ e.stopPropagation();
532
+ if (quickBookConfig && !quickBookConfig.shouldIgnoreToggle()) {
533
+ quickBookConfig.onToggle();
534
+ }
535
+ },
536
+ onMouseEnter: () => {
537
+ if (quickBookConfig?.hover.prefersHover) {
538
+ quickBookConfig.hover.clearClose();
539
+ quickBookConfig.onOpen();
540
+ }
541
+ },
542
+ onMouseLeave: () => {
543
+ if (quickBookConfig?.hover.prefersHover) quickBookConfig.hover.scheduleClose();
544
+ },
545
+ children: [
546
+ /* @__PURE__ */ jsx("div", { className: "absolute top-2 right-2 z-10 pointer-events-none", children: quickBookConfig ? statusDot : /* @__PURE__ */ jsxs(Tooltip, { children: [
547
+ /* @__PURE__ */ jsx(TooltipTrigger, { asChild: true, children: statusDot }),
548
+ /* @__PURE__ */ jsx(TooltipContent, { side: "top", className: "text-xs", children: statusLabel })
549
+ ] }) }),
550
+ /* @__PURE__ */ jsxs("div", { className: "absolute inset-0 flex flex-col justify-between p-3 pointer-events-none", children: [
551
+ /* @__PURE__ */ jsxs("div", { className: "flex items-start gap-2 min-w-0 pr-4", children: [
552
+ /* @__PURE__ */ jsx(
553
+ "div",
554
+ {
555
+ className: cn(
556
+ "shrink-0 rounded-lg flex items-center justify-center",
557
+ m.iconBg,
558
+ m.iconColor
559
+ ),
560
+ style: { width: 28, height: 28 },
561
+ children: /* @__PURE__ */ jsx(TypeIcon, { size: 14, strokeWidth: 2 })
562
+ }
563
+ ),
564
+ /* @__PURE__ */ jsx(
565
+ "span",
566
+ {
567
+ className: "text-xs font-semibold text-foreground leading-tight pt-0.5 line-clamp-2 min-w-0 flex-1",
568
+ style: { wordBreak: "break-word" },
569
+ children: resource.name
570
+ }
571
+ )
572
+ ] }),
573
+ plan && /* @__PURE__ */ jsx(
574
+ "span",
575
+ {
576
+ className: cn(
577
+ "text-[9px] font-medium px-1.5 py-0.5 rounded-md self-start",
578
+ m.badgeBg,
579
+ m.badgeText
580
+ ),
581
+ children: plan.name
582
+ }
583
+ )
584
+ ] })
585
+ ]
586
+ }
587
+ );
588
+ if (!quickBookConfig) return card;
589
+ return /* @__PURE__ */ jsxs(
590
+ Popover,
591
+ {
592
+ modal: false,
593
+ open: quickBookConfig.open,
594
+ onOpenChange: quickBookConfig.onOpenChange,
595
+ children: [
596
+ /* @__PURE__ */ jsx(PopoverAnchor, { asChild: true, children: card }),
597
+ /* @__PURE__ */ jsx(
598
+ PopoverContent,
599
+ {
600
+ side: "top",
601
+ align: "center",
602
+ sideOffset: 6,
603
+ className: CANVAS_QUICK_BOOK_POPOVER_CLASS,
604
+ onOpenAutoFocus: (e) => e.preventDefault(),
605
+ onMouseEnter: quickBookConfig.hover.clearClose,
606
+ onMouseLeave: quickBookConfig.hover.scheduleClose,
607
+ children: /* @__PURE__ */ jsx(
608
+ ResourceQuickBookPopoverContent,
609
+ {
610
+ row,
611
+ onBook: quickBookConfig.quickBook.onBook,
612
+ getBookingHref: quickBookConfig.quickBook.getBookingHref
613
+ }
614
+ )
615
+ }
616
+ )
617
+ ]
618
+ }
619
+ );
620
+ }
621
+ function LayoutCanvasView({
622
+ layout,
623
+ rows,
624
+ onResourceSelect,
625
+ resourceQuickBook,
626
+ searchQuery = "",
627
+ onSearchQueryChange,
628
+ searchPlaceholder = "Search resources\u2026"
629
+ }) {
630
+ const { resolvedTheme } = useTheme();
631
+ const isDarkCanvas = resolvedTheme === "dark";
632
+ const { placedItems, bgImage } = layout;
633
+ const prefersHover = usePrefersHover();
634
+ const canvasSize = useMemo(
635
+ () => ({
636
+ w: layout.canvasWidth ?? DEFAULT_CANVAS_WIDTH,
637
+ h: layout.canvasHeight ?? DEFAULT_CANVAS_HEIGHT
638
+ }),
639
+ [layout.canvasWidth, layout.canvasHeight]
640
+ );
641
+ const [zoom, setZoom] = useState(0.5);
642
+ const [pan, setPan] = useState({ x: 40, y: 40 });
643
+ const [isPanning, setIsPanning] = useState(false);
644
+ const [openResourceId, setOpenResourceId] = useState(null);
645
+ const viewportRef = useRef(null);
646
+ const gestureRef = useRef(null);
647
+ const zoomRef = useRef(zoom);
648
+ const panRef = useRef(pan);
649
+ const popoverCloseTimerRef = useRef(null);
650
+ const suppressResourceToggleRef = useRef(false);
651
+ const clearPopoverCloseTimer = useCallback(() => {
652
+ if (popoverCloseTimerRef.current) {
653
+ clearTimeout(popoverCloseTimerRef.current);
654
+ popoverCloseTimerRef.current = null;
655
+ }
656
+ }, []);
657
+ const schedulePopoverClose = useCallback(() => {
658
+ clearPopoverCloseTimer();
659
+ popoverCloseTimerRef.current = setTimeout(() => {
660
+ setOpenResourceId(null);
661
+ popoverCloseTimerRef.current = null;
662
+ }, POPOVER_CLOSE_DELAY_MS);
663
+ }, [clearPopoverCloseTimer]);
664
+ const popoverHover = useMemo(
665
+ () => ({
666
+ prefersHover,
667
+ clearClose: clearPopoverCloseTimer,
668
+ scheduleClose: schedulePopoverClose
669
+ }),
670
+ [prefersHover, clearPopoverCloseTimer, schedulePopoverClose]
671
+ );
672
+ const openPopover = useCallback(
673
+ (id) => {
674
+ clearPopoverCloseTimer();
675
+ setOpenResourceId(id);
676
+ },
677
+ [clearPopoverCloseTimer]
678
+ );
679
+ const togglePopover = useCallback(
680
+ (id) => {
681
+ clearPopoverCloseTimer();
682
+ setOpenResourceId((current) => current === id ? null : id);
683
+ },
684
+ [clearPopoverCloseTimer]
685
+ );
686
+ useEffect(() => () => clearPopoverCloseTimer(), [clearPopoverCloseTimer]);
687
+ useEffect(() => {
688
+ zoomRef.current = zoom;
689
+ }, [zoom]);
690
+ useEffect(() => {
691
+ panRef.current = pan;
692
+ }, [pan]);
693
+ const rowById = useMemo(() => new Map(rows.map((r) => [r.resource.id, r])), [rows]);
694
+ const otherRows = useMemo(
695
+ () => rows.filter((r) => r.resource.resourceType === "other"),
696
+ [rows]
697
+ );
698
+ const fitCanvas = useCallback(() => {
699
+ const vp = viewportRef.current;
700
+ if (!vp) return;
701
+ const vpRect = vp.getBoundingClientRect();
702
+ const newZoom = Math.max(
703
+ MIN_ZOOM,
704
+ Math.min(
705
+ MAX_ZOOM,
706
+ Math.min((vpRect.width - 80) / canvasSize.w, (vpRect.height - 80) / canvasSize.h)
707
+ )
708
+ );
709
+ setPan({
710
+ x: (vpRect.width - canvasSize.w * newZoom) / 2,
711
+ y: (vpRect.height - canvasSize.h * newZoom) / 2
712
+ });
713
+ setZoom(newZoom);
714
+ }, [canvasSize.h, canvasSize.w]);
715
+ useEffect(() => {
716
+ fitCanvas();
717
+ }, [fitCanvas]);
718
+ useEffect(() => {
719
+ const el = viewportRef.current;
720
+ if (!el) return;
721
+ const onWheel = (e) => {
722
+ e.preventDefault();
723
+ const vpRect = el.getBoundingClientRect();
724
+ const mx = e.clientX - vpRect.left;
725
+ const my = e.clientY - vpRect.top;
726
+ const factor = e.deltaY < 0 ? 1.08 : 1 / 1.08;
727
+ const curZ = zoomRef.current;
728
+ const newZ = Math.max(MIN_ZOOM, Math.min(MAX_ZOOM, curZ * factor));
729
+ const curP = panRef.current;
730
+ setZoom(newZ);
731
+ setPan({
732
+ x: mx - (mx - curP.x) * (newZ / curZ),
733
+ y: my - (my - curP.y) * (newZ / curZ)
734
+ });
735
+ };
736
+ el.addEventListener("wheel", onWheel, { passive: false });
737
+ return () => el.removeEventListener("wheel", onWheel);
738
+ }, []);
739
+ const stepZoom = (delta) => {
740
+ const vp = viewportRef.current;
741
+ const curZ = zoomRef.current;
742
+ const newZ = Math.max(MIN_ZOOM, Math.min(MAX_ZOOM, curZ + delta));
743
+ if (vp) {
744
+ const vpRect = vp.getBoundingClientRect();
745
+ const cx = vpRect.width / 2;
746
+ const cy = vpRect.height / 2;
747
+ const curP = panRef.current;
748
+ setPan({
749
+ x: cx - (cx - curP.x) * (newZ / curZ),
750
+ y: cy - (cy - curP.y) * (newZ / curZ)
751
+ });
752
+ }
753
+ setZoom(newZ);
754
+ };
755
+ useEffect(() => {
756
+ const onKeyDown = (e) => {
757
+ const tag = e.target?.tagName;
758
+ if (tag === "INPUT" || tag === "TEXTAREA" || tag === "SELECT") return;
759
+ if (e.key === "+" || e.key === "=") {
760
+ e.preventDefault();
761
+ stepZoom(0.15);
762
+ } else if (e.key === "-" || e.key === "_") {
763
+ e.preventDefault();
764
+ stepZoom(-0.15);
765
+ }
766
+ };
767
+ window.addEventListener("keydown", onKeyDown);
768
+ return () => window.removeEventListener("keydown", onKeyDown);
769
+ });
770
+ const handleViewportPointerDown = (e) => {
771
+ if (e.button !== 0 && e.button !== 1) return;
772
+ if (isCanvasResourceTarget(e.target)) return;
773
+ clearPopoverCloseTimer();
774
+ setOpenResourceId(null);
775
+ e.preventDefault();
776
+ e.currentTarget.setPointerCapture(e.pointerId);
777
+ setIsPanning(true);
778
+ gestureRef.current = {
779
+ type: "pan",
780
+ startMX: e.clientX,
781
+ startMY: e.clientY,
782
+ startPanX: panRef.current.x,
783
+ startPanY: panRef.current.y
784
+ };
785
+ };
786
+ const handleViewportPointerMove = (e) => {
787
+ const g = gestureRef.current;
788
+ if (g?.type !== "pan") return;
789
+ setPan({
790
+ x: g.startPanX + (e.clientX - g.startMX),
791
+ y: g.startPanY + (e.clientY - g.startMY)
792
+ });
793
+ };
794
+ const handleViewportPointerUp = () => {
795
+ if (gestureRef.current?.type === "pan") {
796
+ gestureRef.current = null;
797
+ setIsPanning(false);
798
+ }
799
+ };
800
+ const handleSelect = (id) => onResourceSelect?.(id);
801
+ const quickBookConfigFor = useCallback(
802
+ (resourceId) => {
803
+ if (!resourceQuickBook) return void 0;
804
+ const wrappedOnBook = resourceQuickBook.onBook ? (id) => {
805
+ suppressResourceToggleRef.current = true;
806
+ clearPopoverCloseTimer();
807
+ setOpenResourceId(null);
808
+ resourceQuickBook.onBook?.(id);
809
+ window.setTimeout(() => {
810
+ suppressResourceToggleRef.current = false;
811
+ }, 400);
812
+ } : void 0;
813
+ return {
814
+ quickBook: {
815
+ ...resourceQuickBook,
816
+ onBook: wrappedOnBook
817
+ },
818
+ open: openResourceId === resourceId,
819
+ onOpenChange: (open) => {
820
+ clearPopoverCloseTimer();
821
+ setOpenResourceId(open ? resourceId : null);
822
+ },
823
+ hover: popoverHover,
824
+ onOpen: () => openPopover(resourceId),
825
+ onToggle: () => togglePopover(resourceId),
826
+ shouldIgnoreToggle: () => suppressResourceToggleRef.current
827
+ };
828
+ },
829
+ [
830
+ resourceQuickBook,
831
+ openResourceId,
832
+ clearPopoverCloseTimer,
833
+ popoverHover,
834
+ openPopover,
835
+ togglePopover
836
+ ]
837
+ );
838
+ return /* @__PURE__ */ jsxs("div", { className: "flex flex-col flex-1 min-h-0 bg-card", children: [
839
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center gap-2 p-2 border-b border-border bg-card shrink-0", children: [
840
+ onSearchQueryChange != null ? /* @__PURE__ */ jsx(
841
+ PageListSearchField,
842
+ {
843
+ value: searchQuery,
844
+ onChange: onSearchQueryChange,
845
+ placeholder: searchPlaceholder,
846
+ className: "min-w-0 flex-1 max-w-md h-9"
847
+ }
848
+ ) : null,
849
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1 shrink-0 ml-auto", children: [
850
+ /* @__PURE__ */ jsx(
851
+ "button",
852
+ {
853
+ type: "button",
854
+ onClick: () => stepZoom(-0.15),
855
+ className: "w-7 h-7 rounded-lg border border-border bg-card flex items-center justify-center text-muted-foreground hover:bg-accent transition-colors",
856
+ "aria-label": "Zoom out",
857
+ children: /* @__PURE__ */ jsx(Minus, { size: 12 })
858
+ }
859
+ ),
860
+ /* @__PURE__ */ jsxs("span", { className: "text-xs text-muted-foreground w-10 text-center tabular-nums select-none", children: [
861
+ Math.round(zoom * 100),
862
+ "%"
863
+ ] }),
864
+ /* @__PURE__ */ jsx(
865
+ "button",
866
+ {
867
+ type: "button",
868
+ onClick: () => stepZoom(0.15),
869
+ className: "w-7 h-7 rounded-lg border border-border bg-card flex items-center justify-center text-muted-foreground hover:bg-accent transition-colors",
870
+ "aria-label": "Zoom in",
871
+ children: /* @__PURE__ */ jsx(Plus, { size: 12 })
872
+ }
873
+ ),
874
+ /* @__PURE__ */ jsxs(
875
+ "button",
876
+ {
877
+ type: "button",
878
+ onClick: fitCanvas,
879
+ className: "inline-flex items-center gap-1 px-2.5 h-7 rounded-lg border border-border bg-card text-xs text-muted-foreground hover:bg-accent transition-colors",
880
+ children: [
881
+ /* @__PURE__ */ jsx(Maximize2, { size: 11 }),
882
+ "Fit"
883
+ ]
884
+ }
885
+ )
886
+ ] })
887
+ ] }),
888
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-col flex-1 min-w-0 min-h-0", children: [
889
+ /* @__PURE__ */ jsx(
890
+ "div",
891
+ {
892
+ ref: viewportRef,
893
+ tabIndex: 0,
894
+ className: "flex-1 min-h-[420px] overflow-hidden relative bg-card outline-none",
895
+ style: { cursor: isPanning ? "grabbing" : "grab" },
896
+ onPointerDown: handleViewportPointerDown,
897
+ onPointerMove: handleViewportPointerMove,
898
+ onPointerUp: handleViewportPointerUp,
899
+ onPointerCancel: handleViewportPointerUp,
900
+ children: /* @__PURE__ */ jsxs(
901
+ "div",
902
+ {
903
+ style: {
904
+ position: "absolute",
905
+ transformOrigin: "0px 0px",
906
+ transform: `translate(${pan.x}px, ${pan.y}px) scale(${zoom})`,
907
+ width: canvasSize.w,
908
+ height: canvasSize.h,
909
+ backgroundImage: bgImage ? "none" : canvasGridBackground(isDarkCanvas),
910
+ backgroundSize: bgImage ? "auto" : CANVAS_GRID_BG_SIZE,
911
+ backgroundColor: "var(--card)",
912
+ boxShadow: isDarkCanvas ? "0 2px 16px rgba(0,0,0,0.45)" : "0 2px 16px rgba(0,0,0,0.12)"
913
+ },
914
+ children: [
915
+ bgImage && /* @__PURE__ */ jsx(
916
+ "img",
917
+ {
918
+ src: bgImage,
919
+ alt: "Floor plan",
920
+ draggable: false,
921
+ style: {
922
+ position: "absolute",
923
+ inset: 0,
924
+ width: "100%",
925
+ height: "100%",
926
+ objectFit: "cover",
927
+ pointerEvents: "none",
928
+ userSelect: "none"
929
+ }
930
+ }
931
+ ),
932
+ Object.entries(placedItems).map(([id, rect]) => {
933
+ const row = rowById.get(id);
934
+ if (!row || !isFloorResourceType(row.resource.resourceType)) return null;
935
+ return /* @__PURE__ */ jsx(
936
+ PlacedCardView,
937
+ {
938
+ id,
939
+ rect,
940
+ row,
941
+ onSelect: resourceQuickBook ? void 0 : handleSelect,
942
+ quickBookConfig: quickBookConfigFor(id),
943
+ searchQuery,
944
+ isMatch: resourceMatchesSearch(row, searchQuery)
945
+ },
946
+ id
947
+ );
948
+ })
949
+ ]
950
+ }
951
+ )
952
+ }
953
+ ),
954
+ otherRows.length > 0 && /* @__PURE__ */ jsx("div", { className: "shrink-0 border-t border-border bg-card px-4 py-2.5", children: /* @__PURE__ */ jsx("div", { className: "flex items-center gap-2 overflow-x-auto pb-0.5", children: otherRows.map((row) => {
955
+ const resourceId = row.resource.id;
956
+ return /* @__PURE__ */ jsx(
957
+ PoolResourceChip,
958
+ {
959
+ row,
960
+ onClick: resourceQuickBook ? void 0 : () => handleSelect(resourceId),
961
+ quickBookConfig: quickBookConfigFor(resourceId),
962
+ searchQuery,
963
+ isMatch: resourceMatchesSearch(row, searchQuery)
964
+ },
965
+ resourceId
966
+ );
967
+ }) }) })
968
+ ] })
969
+ ] });
970
+ }
971
+
972
+ export { LayoutCanvasView, matchesCanvasResourceSearch };
973
+ //# sourceMappingURL=layout-canvas-view.js.map
974
+ //# sourceMappingURL=layout-canvas-view.js.map