@bizapps/design-system-testing 1.10.0 → 1.11.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.
package/dist/main.cjs CHANGED
@@ -2243,10 +2243,10 @@ function createStore() {
2243
2243
  listeners: [],
2244
2244
  add(item, id) {
2245
2245
  const toastId = id ?? Math.random().toString(36).slice(2);
2246
- const exists = store.toasts.find((t) => t.id === toastId);
2246
+ const exists = store.toasts.find((t2) => t2.id === toastId);
2247
2247
  if (exists) {
2248
2248
  store.toasts = store.toasts.map(
2249
- (t) => t.id === toastId ? { ...t, ...item, id: toastId } : t
2249
+ (t2) => t2.id === toastId ? { ...t2, ...item, id: toastId } : t2
2250
2250
  );
2251
2251
  } else {
2252
2252
  store.toasts = [{ id: toastId, ...item }, ...store.toasts].slice(0, MAX_TOASTS);
@@ -2261,7 +2261,7 @@ function createStore() {
2261
2261
  remove(id) {
2262
2262
  clearTimeout(timers.get(id));
2263
2263
  timers.delete(id);
2264
- store.toasts = store.toasts.filter((t) => t.id !== id);
2264
+ store.toasts = store.toasts.filter((t2) => t2.id !== id);
2265
2265
  store.listeners.forEach((l) => l([...store.toasts]));
2266
2266
  },
2267
2267
  subscribe(listener) {
@@ -2405,7 +2405,7 @@ function Toaster({ position = "bottom-right", children }) {
2405
2405
  "pointer-events-none fixed z-[100] flex flex-col gap-2",
2406
2406
  positionStyles[position]
2407
2407
  ),
2408
- children: /* @__PURE__ */ jsxRuntime.jsx(react$1.AnimatePresence, { mode: "sync", initial: false, children: toasts.map((t) => /* @__PURE__ */ jsxRuntime.jsx("div", { className: "pointer-events-auto", children: /* @__PURE__ */ jsxRuntime.jsx(ToastCard, { ...t }) }, t.id)) })
2408
+ children: /* @__PURE__ */ jsxRuntime.jsx(react$1.AnimatePresence, { mode: "sync", initial: false, children: toasts.map((t2) => /* @__PURE__ */ jsxRuntime.jsx("div", { className: "pointer-events-auto", children: /* @__PURE__ */ jsxRuntime.jsx(ToastCard, { ...t2 }) }, t2.id)) })
2409
2409
  }
2410
2410
  )
2411
2411
  ] });
@@ -5186,13 +5186,11 @@ function AbilityProvider({ children, permissions }) {
5186
5186
  setImperativelyResolved(true);
5187
5187
  }, []);
5188
5188
  const permissionKey = permissions === void 0 ? null : [...permissions].sort().join("|");
5189
- const syncedKey = react.useRef(permissionKey);
5190
- react.useEffect(() => {
5191
- if (permissionKey === null) return;
5192
- if (permissionKey === syncedKey.current) return;
5193
- syncedKey.current = permissionKey;
5189
+ const [syncedKey, setSyncedKey] = react.useState(permissionKey);
5190
+ if (permissionKey !== null && permissionKey !== syncedKey) {
5191
+ setSyncedKey(permissionKey);
5194
5192
  setPermissionSet(new Set(permissions));
5195
- }, [permissionKey, permissions]);
5193
+ }
5196
5194
  const value = react.useMemo(
5197
5195
  () => ({ can: can2, setAbilities, clearAbilities, status }),
5198
5196
  [can2, setAbilities, clearAbilities, status]
@@ -5206,6 +5204,9 @@ function useAbility() {
5206
5204
  }
5207
5205
  return context;
5208
5206
  }
5207
+ function useOptionalAbility() {
5208
+ return react.useContext(AbilityContext);
5209
+ }
5209
5210
  function useCan(permissions, mode = "any") {
5210
5211
  const { can: can2 } = useAbility();
5211
5212
  return can2(permissions, mode);
@@ -9200,6 +9201,1034 @@ function BottomNavTabsList({ items, className }) {
9200
9201
  }
9201
9202
  );
9202
9203
  }
9204
+ var DEFAULT_LABELS = {
9205
+ navigation: "Modules navigation",
9206
+ title: "Modules",
9207
+ collapse: "Collapse menu",
9208
+ expand: "Expand menu",
9209
+ openMenu: "Open menu",
9210
+ currentPageSuffix: " (contains the current page)",
9211
+ searchPlaceholder: () => "Filter links\u2026",
9212
+ searchLabel: (moduleLabel) => `Filter links of ${moduleLabel}`,
9213
+ clearSearch: "Clear search",
9214
+ noMatchesTitle: "No matches",
9215
+ noMatches: (moduleLabel, query) => `No links in ${moduleLabel} match \u201C${query}\u201D.`,
9216
+ flyoutNoMatches: (query) => `No links match \u201C${query}\u201D.`,
9217
+ emptyModule: "No links in this module yet.",
9218
+ addFavorite: (itemLabel) => `Add to favorites: ${itemLabel}`,
9219
+ removeFavorite: (itemLabel) => `Remove from favorites: ${itemLabel}`
9220
+ };
9221
+ var EASE = "cubic-bezier(.2,.8,.2,1)";
9222
+ var MOTION_MS = 240;
9223
+ var FADE_MS = MOTION_MS / 2;
9224
+ var BOUNCE_KEYFRAMES = [
9225
+ { transform: "translateY(0) scale(1)" },
9226
+ { transform: "translateY(-3px) scale(1.06)", offset: 0.35 },
9227
+ { transform: "translateY(0) scale(0.97)", offset: 0.65 },
9228
+ { transform: "translateY(0) scale(1)" }
9229
+ ];
9230
+ var BOUNCE_MS = 420;
9231
+ var SUBTLE_SCROLLBAR = "[scrollbar-color:var(--floating-sidebar-scrollbar-thumb)_var(--floating-sidebar-scrollbar-track)]! [&::-webkit-scrollbar]:w-2.5! [&::-webkit-scrollbar-track]:bg-floating-sidebar-scrollbar-track! [&::-webkit-scrollbar-thumb]:rounded-full! [&::-webkit-scrollbar-thumb]:border-2! [&::-webkit-scrollbar-thumb]:border-solid! [&::-webkit-scrollbar-thumb]:border-floating-sidebar-scrollbar-track! [&::-webkit-scrollbar-thumb]:bg-floating-sidebar-scrollbar-thumb! [&::-webkit-scrollbar-thumb:hover]:bg-floating-sidebar-scrollbar-thumb-hover!";
9232
+ var DEFAULT_WIDTH = 276;
9233
+ var DEFAULT_COLLAPSED_WIDTH = 48;
9234
+ var MIN_COLLAPSED_WIDTH = 40;
9235
+ var t = (prop, ms = 300, delay = 0) => `${prop} ${ms}ms ${EASE} ${delay}ms`;
9236
+ function transition2(reduced, ...parts) {
9237
+ const kept = reduced ? parts.filter((part) => /^(opacity|visibility|background|color|box-shadow)/.test(part)) : parts;
9238
+ return kept.join(", ") || "none";
9239
+ }
9240
+ function useControllableState2(value, defaultValue, onChange) {
9241
+ const [internal, setInternal] = react.useState(defaultValue);
9242
+ const isControlled = value !== void 0;
9243
+ const current = isControlled ? value : internal;
9244
+ const setValue = react.useCallback(
9245
+ (next) => {
9246
+ if (!isControlled) setInternal(next);
9247
+ onChange?.(next);
9248
+ },
9249
+ [isControlled, onChange]
9250
+ );
9251
+ return [current, setValue];
9252
+ }
9253
+ var normalize = (value) => value.normalize("NFD").replace(/[̀-ͯ]/g, "").toLowerCase();
9254
+ function filterItems(items, query) {
9255
+ const needle = normalize(query.trim());
9256
+ return needle ? items.filter((item) => normalize(item.label).includes(needle)) : items;
9257
+ }
9258
+ var lastUsedSidebar = null;
9259
+ var SHORTCUT_SIDEBAR = '[data-slot="floating-sidebar"][data-shortcut]';
9260
+ function isEditableTarget(target) {
9261
+ if (!(target instanceof HTMLElement)) return false;
9262
+ if (target.closest('[data-slot="floating-sidebar-search"]')) return false;
9263
+ return target.closest('[contenteditable]:not([contenteditable="false"])') !== null || target.tagName === "INPUT" || target.tagName === "TEXTAREA" || target.tagName === "SELECT";
9264
+ }
9265
+ function SidebarIcon({
9266
+ icon,
9267
+ resolveIcon,
9268
+ small = false
9269
+ }) {
9270
+ const resolved = typeof icon === "string" ? resolveIcon?.(icon) : icon;
9271
+ if (resolved === void 0 || resolved === null) {
9272
+ return /* @__PURE__ */ jsxRuntime.jsx("span", { "data-slot": "floating-sidebar-fallback-icon", className: "flex", children: /* @__PURE__ */ jsxRuntime.jsx(
9273
+ lucideReact.CircleDashed,
9274
+ {
9275
+ "aria-hidden": "true",
9276
+ className: small ? "size-[18px]" : "size-5",
9277
+ strokeWidth: 1.5
9278
+ }
9279
+ ) });
9280
+ }
9281
+ return /* @__PURE__ */ jsxRuntime.jsx(
9282
+ "span",
9283
+ {
9284
+ "data-slot": "floating-sidebar-icon",
9285
+ className: cn(
9286
+ "flex",
9287
+ "[&>svg]:stroke-[1.5]",
9288
+ small ? "[&>svg]:size-[18px]" : "[&>svg]:size-5"
9289
+ ),
9290
+ children: resolved
9291
+ }
9292
+ );
9293
+ }
9294
+ function FloatingSidebar({
9295
+ modules,
9296
+ resolveIcon,
9297
+ renderLink,
9298
+ open: openProp,
9299
+ defaultOpen = true,
9300
+ onOpenChange,
9301
+ side = "left",
9302
+ width = DEFAULT_WIDTH,
9303
+ collapsedWidth = DEFAULT_COLLAPSED_WIDTH,
9304
+ type = "single",
9305
+ openModuleIds: openModulesProp,
9306
+ defaultOpenModuleIds = [],
9307
+ onOpenModulesChange,
9308
+ hideEmptyModules = true,
9309
+ activeItemId: activeItemProp,
9310
+ defaultActiveItemId = null,
9311
+ onItemSelect,
9312
+ favoriteIds: favoritesProp,
9313
+ defaultFavoriteIds = [],
9314
+ onFavoritesChange,
9315
+ showFavorites = true,
9316
+ loading = false,
9317
+ emptyState,
9318
+ mobileOpen: mobileOpenProp,
9319
+ defaultMobileOpen = false,
9320
+ onMobileOpenChange,
9321
+ shortcut = true,
9322
+ labels: labelsProp,
9323
+ className,
9324
+ ref,
9325
+ style,
9326
+ onPointerDownCapture,
9327
+ onFocusCapture,
9328
+ ...navProps
9329
+ }) {
9330
+ const labels = { ...DEFAULT_LABELS, ...labelsProp };
9331
+ const reduced = useMediaQuery("(prefers-reduced-motion: reduce)");
9332
+ const isMobile = useIsMobile();
9333
+ const baseId = react.useId();
9334
+ const [open, setOpen] = useControllableState2(openProp, defaultOpen, onOpenChange);
9335
+ const [rawOpenModuleIds, setOpenModuleIds] = useControllableState2(
9336
+ openModulesProp,
9337
+ defaultOpenModuleIds,
9338
+ onOpenModulesChange
9339
+ );
9340
+ const railWidth = Math.max(MIN_COLLAPSED_WIDTH, collapsedWidth);
9341
+ const [activeItemId, setActiveItemId] = useControllableState2(
9342
+ activeItemProp,
9343
+ defaultActiveItemId
9344
+ );
9345
+ const [favoriteIds, setFavoriteIds] = useControllableState2(
9346
+ favoritesProp,
9347
+ defaultFavoriteIds,
9348
+ onFavoritesChange
9349
+ );
9350
+ const [mobileOpen, setMobileOpen] = useControllableState2(
9351
+ mobileOpenProp,
9352
+ defaultMobileOpen,
9353
+ onMobileOpenChange
9354
+ );
9355
+ const [queries, setQueries] = react.useState({});
9356
+ const [flyoutId, setFlyoutId] = react.useState(null);
9357
+ const [hoverId, setHoverId] = react.useState(null);
9358
+ const collapsed = !isMobile && !open;
9359
+ const [prevCollapsed, setPrevCollapsed] = react.useState(collapsed);
9360
+ const [settling, setSettling] = react.useState(false);
9361
+ if (prevCollapsed !== collapsed) {
9362
+ setPrevCollapsed(collapsed);
9363
+ setSettling(collapsed);
9364
+ setFlyoutId(null);
9365
+ setHoverId(null);
9366
+ }
9367
+ react.useEffect(() => {
9368
+ if (!settling) return;
9369
+ const timer = setTimeout(() => setSettling(false), MOTION_MS);
9370
+ return () => clearTimeout(timer);
9371
+ }, [settling]);
9372
+ const toggle = react.useCallback(() => setOpen(!open), [open, setOpen]);
9373
+ const navRef = react.useRef(null);
9374
+ react.useEffect(() => {
9375
+ if (!shortcut) return;
9376
+ const onKeyDown = (event) => {
9377
+ if (event.defaultPrevented || isEditableTarget(event.target)) return;
9378
+ if ((event.ctrlKey || event.metaKey) && !event.shiftKey && !event.altKey && event.key.toLowerCase() === "b") {
9379
+ const owner = event.target instanceof Element ? event.target.closest(SHORTCUT_SIDEBAR) : null;
9380
+ const nav2 = navRef.current;
9381
+ if (owner && owner !== nav2) return;
9382
+ if (!owner) {
9383
+ const fallback = lastUsedSidebar?.isConnected && lastUsedSidebar.matches(SHORTCUT_SIDEBAR) ? lastUsedSidebar : document.querySelector(SHORTCUT_SIDEBAR);
9384
+ if (fallback && fallback !== nav2) return;
9385
+ }
9386
+ event.preventDefault();
9387
+ if (isMobile) setMobileOpen(!mobileOpen);
9388
+ else toggle();
9389
+ }
9390
+ };
9391
+ document.addEventListener("keydown", onKeyDown);
9392
+ return () => document.removeEventListener("keydown", onKeyDown);
9393
+ }, [shortcut, toggle, isMobile, mobileOpen, setMobileOpen]);
9394
+ const selectItem = (item, module, navigates) => {
9395
+ if (navigates) setActiveItemId(item.id);
9396
+ setFlyoutId(null);
9397
+ if (isMobile) setMobileOpen(false);
9398
+ onItemSelect?.(item, module);
9399
+ };
9400
+ const toggleFavorite = (itemId) => setFavoriteIds(
9401
+ favoriteIds.includes(itemId) ? favoriteIds.filter((id) => id !== itemId) : [...favoriteIds, itemId]
9402
+ );
9403
+ const list = {
9404
+ activeItemId,
9405
+ favoriteIds: showFavorites ? favoriteIds : null,
9406
+ reduced,
9407
+ labels,
9408
+ resolveIcon,
9409
+ renderLink,
9410
+ onToggleFavorite: toggleFavorite
9411
+ };
9412
+ const toggleLabel = collapsed ? labels.expand : labels.collapse;
9413
+ const ability = useOptionalAbility();
9414
+ const usesPermissions = modules.some(
9415
+ (module) => module.permission || module.items.some((item) => item.permission)
9416
+ );
9417
+ const permissionsPending = usesPermissions && ability?.status === "loading";
9418
+ const isAllowed = (entry) => !entry.permission || ability?.status === "ready" && ability.can(entry.permission, entry.permissionMode);
9419
+ const authorizedModules = usesPermissions ? modules.filter(isAllowed).flatMap((module) => {
9420
+ const items = module.items.filter(isAllowed);
9421
+ return items.length > 0 || module.items.length === 0 ? [{ ...module, items }] : [];
9422
+ }) : modules;
9423
+ const showSkeletons = loading || permissionsPending;
9424
+ const visibleModules = hideEmptyModules ? authorizedModules.filter((module) => module.items.length > 0) : authorizedModules;
9425
+ const visibleModuleIds = new Set(visibleModules.map((module) => module.id));
9426
+ const openModuleIds = type === "single" ? rawOpenModuleIds.filter((id) => visibleModuleIds.has(id)).slice(0, 1) : rawOpenModuleIds;
9427
+ const toggleModule = (moduleId) => {
9428
+ const isOpen = openModuleIds.includes(moduleId);
9429
+ if (type === "multiple") {
9430
+ setOpenModuleIds(
9431
+ isOpen ? openModuleIds.filter((id) => id !== moduleId) : [...openModuleIds, moduleId]
9432
+ );
9433
+ } else {
9434
+ setOpenModuleIds(isOpen ? [] : [moduleId]);
9435
+ }
9436
+ };
9437
+ const nav = /* @__PURE__ */ jsxRuntime.jsxs(
9438
+ "nav",
9439
+ {
9440
+ ...navProps,
9441
+ ref: (node) => {
9442
+ navRef.current = node;
9443
+ if (typeof ref === "function") return ref(node);
9444
+ if (ref) ref.current = node;
9445
+ },
9446
+ onPointerDownCapture: (event) => {
9447
+ lastUsedSidebar = event.currentTarget;
9448
+ onPointerDownCapture?.(event);
9449
+ },
9450
+ onFocusCapture: (event) => {
9451
+ lastUsedSidebar = event.currentTarget;
9452
+ onFocusCapture?.(event);
9453
+ },
9454
+ "data-slot": "floating-sidebar",
9455
+ "data-shortcut": shortcut ? "" : void 0,
9456
+ "data-state": collapsed ? "collapsed" : "expanded",
9457
+ "data-side": side,
9458
+ "aria-label": labels.navigation,
9459
+ "aria-busy": showSkeletons || void 0,
9460
+ className: cn(
9461
+ "flex h-full min-h-0 flex-col gap-3",
9462
+ // useIsMobile only resolves after mount; hide the inline layout on phones with CSS
9463
+ // so the first paint never shows it before the drawer takes over.
9464
+ !isMobile && "max-md:hidden",
9465
+ className
9466
+ ),
9467
+ style: isMobile ? style : {
9468
+ ...style,
9469
+ width: collapsed ? railWidth : width,
9470
+ transition: transition2(reduced, t("width", MOTION_MS))
9471
+ },
9472
+ children: [
9473
+ /* @__PURE__ */ jsxRuntime.jsxs(
9474
+ "div",
9475
+ {
9476
+ "data-slot": "floating-sidebar-header",
9477
+ className: cn(
9478
+ "flex h-10 shrink-0 items-center",
9479
+ side === "right" && "flex-row-reverse"
9480
+ ),
9481
+ children: [
9482
+ /* @__PURE__ */ jsxRuntime.jsx(
9483
+ "span",
9484
+ {
9485
+ title: labels.title,
9486
+ className: "text-muted-foreground overflow-hidden pl-1.5 text-[11px] font-medium tracking-[.12em] text-ellipsis whitespace-nowrap uppercase",
9487
+ style: {
9488
+ opacity: collapsed ? 0 : 1,
9489
+ maxWidth: collapsed ? 0 : 200,
9490
+ transition: transition2(
9491
+ reduced,
9492
+ t("opacity", FADE_MS, collapsed ? 0 : FADE_MS),
9493
+ t("max-width", MOTION_MS)
9494
+ )
9495
+ },
9496
+ children: labels.title
9497
+ }
9498
+ ),
9499
+ !isMobile && /* @__PURE__ */ jsxRuntime.jsx(
9500
+ "button",
9501
+ {
9502
+ type: "button",
9503
+ "data-slot": "floating-sidebar-toggle",
9504
+ "aria-label": toggleLabel,
9505
+ "aria-expanded": !collapsed,
9506
+ title: shortcut ? `${toggleLabel} (Ctrl/\u2318 + B)` : toggleLabel,
9507
+ onClick: toggle,
9508
+ className: cn(
9509
+ "text-primary focus-visible:ring-primary bg-sidebar border-floating-sidebar-card-border hover:bg-floating-sidebar-toggle-hover flex size-8 shrink-0 cursor-pointer items-center justify-center rounded-full border shadow-[0_2px_6px_color-mix(in_srgb,var(--color-primary)_6%,transparent)] outline-none focus-visible:ring-2 focus-visible:ring-offset-2",
9510
+ side === "right" ? "mr-auto" : "ml-auto"
9511
+ ),
9512
+ style: {
9513
+ // The auto margin keeps the toggle on the edge while the width animates;
9514
+ // only the outer margin changes, to center it on the collapsed rail.
9515
+ // (`auto` cannot be interpolated, so animating it made the toggle jump.)
9516
+ [side === "right" ? "marginLeft" : "marginRight"]: collapsed ? (railWidth - 32) / 2 : 0,
9517
+ transition: transition2(
9518
+ reduced,
9519
+ t(side === "right" ? "margin-left" : "margin-right", MOTION_MS)
9520
+ )
9521
+ },
9522
+ children: /* @__PURE__ */ jsxRuntime.jsx(
9523
+ "span",
9524
+ {
9525
+ className: "flex",
9526
+ style: {
9527
+ // The chevrons point towards the edge when they collapse the sidebar.
9528
+ transform: collapsed !== (side === "right") ? "rotate(180deg)" : "rotate(0deg)",
9529
+ transition: transition2(reduced, t("transform", MOTION_MS))
9530
+ },
9531
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronsLeft, { "aria-hidden": "true", className: "size-5", strokeWidth: 1.5 })
9532
+ }
9533
+ )
9534
+ }
9535
+ )
9536
+ ]
9537
+ }
9538
+ ),
9539
+ /* @__PURE__ */ jsxRuntime.jsxs(
9540
+ "div",
9541
+ {
9542
+ "data-slot": "floating-sidebar-modules",
9543
+ className: "flex min-h-0 flex-1 flex-col",
9544
+ style: {
9545
+ gap: collapsed ? 8 : 12,
9546
+ transition: transition2(reduced, t("gap", MOTION_MS))
9547
+ },
9548
+ children: [
9549
+ showSkeletons && Array.from({ length: 4 }, (_, index) => /* @__PURE__ */ jsxRuntime.jsx("div", { "data-slot": "floating-sidebar-skeleton", children: /* @__PURE__ */ jsxRuntime.jsx(
9550
+ Skeleton,
9551
+ {
9552
+ className: collapsed ? "rounded-[14px]" : "h-[58px] w-full rounded-2xl",
9553
+ style: collapsed ? { width: railWidth, height: railWidth } : void 0
9554
+ }
9555
+ ) }, index)),
9556
+ !showSkeletons && visibleModules.length === 0 && emptyState,
9557
+ !showSkeletons && visibleModules.map((module) => /* @__PURE__ */ jsxRuntime.jsx(
9558
+ ModuleCard,
9559
+ {
9560
+ tileSize: railWidth,
9561
+ module,
9562
+ regionId: `${baseId}-${module.id}`,
9563
+ flyoutRegionId: `${baseId}-${module.id}-flyout`,
9564
+ bodyWidth: isMobile ? void 0 : width - 2,
9565
+ collapsed,
9566
+ side,
9567
+ open: !collapsed && openModuleIds.includes(module.id),
9568
+ flyoutOpen: collapsed && flyoutId === module.id,
9569
+ anyFlyoutOpen: collapsed && flyoutId !== null,
9570
+ settling,
9571
+ tooltipOpen: collapsed && hoverId === module.id && !flyoutId,
9572
+ onTooltipOpenChange: (next) => setHoverId(
9573
+ (current) => next ? module.id : current === module.id ? null : current
9574
+ ),
9575
+ onFlyoutClose: () => setFlyoutId((current) => current === module.id ? null : current),
9576
+ list,
9577
+ query: queries[module.id] ?? "",
9578
+ onQueryChange: (query) => setQueries((current) => ({ ...current, [module.id]: query })),
9579
+ onHeaderClick: () => {
9580
+ if (collapsed) {
9581
+ setHoverId(null);
9582
+ setFlyoutId(flyoutId === module.id ? null : module.id);
9583
+ } else {
9584
+ toggleModule(module.id);
9585
+ }
9586
+ },
9587
+ onSelect: (item, navigates) => selectItem(item, module, navigates)
9588
+ },
9589
+ module.id
9590
+ ))
9591
+ ]
9592
+ }
9593
+ )
9594
+ ]
9595
+ }
9596
+ );
9597
+ if (isMobile) {
9598
+ return /* @__PURE__ */ jsxRuntime.jsx(TooltipProvider, { delayDuration: 300, children: /* @__PURE__ */ jsxRuntime.jsxs(Sheet, { open: mobileOpen, onOpenChange: setMobileOpen, children: [
9599
+ mobileOpenProp === void 0 && /* @__PURE__ */ jsxRuntime.jsx(SheetTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(
9600
+ "button",
9601
+ {
9602
+ type: "button",
9603
+ "data-slot": "floating-sidebar-mobile-trigger",
9604
+ "aria-label": labels.openMenu,
9605
+ className: "text-primary focus-visible:ring-primary bg-sidebar hover:bg-sidebar-accent flex size-10 cursor-pointer items-center justify-center rounded-[10px] shadow-[0_8px_24px_color-mix(in_srgb,var(--color-primary)_8%,transparent)] outline-none focus-visible:ring-2 focus-visible:ring-offset-2",
9606
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Menu, { "aria-hidden": "true", className: "size-5", strokeWidth: 1.5 })
9607
+ }
9608
+ ) }),
9609
+ /* @__PURE__ */ jsxRuntime.jsxs(
9610
+ SheetContent,
9611
+ {
9612
+ side,
9613
+ "aria-describedby": void 0,
9614
+ className: "p-4 pt-12",
9615
+ onEscapeKeyDown: (event) => {
9616
+ const active = document.activeElement;
9617
+ if (active instanceof HTMLInputElement && active.value && active.closest('[data-slot="floating-sidebar-search"]')) {
9618
+ event.preventDefault();
9619
+ }
9620
+ },
9621
+ children: [
9622
+ /* @__PURE__ */ jsxRuntime.jsx(SheetTitle, { className: "sr-only", children: labels.title }),
9623
+ nav
9624
+ ]
9625
+ }
9626
+ )
9627
+ ] }) });
9628
+ }
9629
+ return /* @__PURE__ */ jsxRuntime.jsx(TooltipProvider, { delayDuration: 300, children: nav });
9630
+ }
9631
+ function ModuleSearch({
9632
+ moduleLabel,
9633
+ query,
9634
+ focusable,
9635
+ labels,
9636
+ onQueryChange,
9637
+ className
9638
+ }) {
9639
+ const tabIndex = focusable ? 0 : -1;
9640
+ const inputRef = react.useRef(null);
9641
+ return /* @__PURE__ */ jsxRuntime.jsxs(
9642
+ "div",
9643
+ {
9644
+ "data-slot": "floating-sidebar-search",
9645
+ className: cn(
9646
+ "text-muted-foreground border-floating-sidebar-search-border bg-floating-sidebar-search flex h-[38px] items-center gap-2 rounded-[10px] border pr-1 pl-3",
9647
+ className
9648
+ ),
9649
+ children: [
9650
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Search, { "aria-hidden": "true", className: "size-4 shrink-0", strokeWidth: 1.5 }),
9651
+ /* @__PURE__ */ jsxRuntime.jsx(
9652
+ "input",
9653
+ {
9654
+ ref: inputRef,
9655
+ type: "search",
9656
+ "aria-label": labels.searchLabel(moduleLabel),
9657
+ placeholder: labels.searchPlaceholder(moduleLabel),
9658
+ tabIndex,
9659
+ value: query,
9660
+ onChange: (event) => onQueryChange(event.target.value),
9661
+ onKeyDown: (event) => {
9662
+ if (event.key === "Escape" && query) {
9663
+ event.stopPropagation();
9664
+ onQueryChange("");
9665
+ }
9666
+ },
9667
+ className: "text-foreground placeholder:text-muted-foreground h-[34px] min-w-0 flex-1 border-0 bg-transparent p-0 text-[13px] outline-none [&::-webkit-search-cancel-button]:appearance-none"
9668
+ }
9669
+ ),
9670
+ query && /* @__PURE__ */ jsxRuntime.jsx(
9671
+ "button",
9672
+ {
9673
+ type: "button",
9674
+ "aria-label": labels.clearSearch,
9675
+ tabIndex,
9676
+ onClick: () => {
9677
+ onQueryChange("");
9678
+ inputRef.current?.focus();
9679
+ },
9680
+ className: "focus-visible:ring-primary hover:bg-accent/60 flex size-8 shrink-0 cursor-pointer items-center justify-center rounded-lg border-0 bg-transparent outline-none focus-visible:ring-2",
9681
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.X, { "aria-hidden": "true", className: "size-4", strokeWidth: 1.5 })
9682
+ }
9683
+ )
9684
+ ]
9685
+ }
9686
+ );
9687
+ }
9688
+ function NoMatches({
9689
+ show,
9690
+ moduleLabel,
9691
+ query,
9692
+ labels,
9693
+ compact
9694
+ }) {
9695
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { role: "status", "data-slot": "floating-sidebar-no-matches", children: show && /* @__PURE__ */ jsxRuntime.jsx(
9696
+ NoMatchesMessage,
9697
+ {
9698
+ moduleLabel,
9699
+ query,
9700
+ labels,
9701
+ compact
9702
+ }
9703
+ ) });
9704
+ }
9705
+ function NoMatchesMessage({
9706
+ moduleLabel,
9707
+ query,
9708
+ labels,
9709
+ compact = false
9710
+ }) {
9711
+ return /* @__PURE__ */ jsxRuntime.jsxs(
9712
+ "div",
9713
+ {
9714
+ className: cn(
9715
+ "flex flex-col items-center text-center",
9716
+ compact ? "gap-1 px-3 py-4" : "gap-1.5 px-3 py-5"
9717
+ ),
9718
+ children: [
9719
+ !compact && /* @__PURE__ */ jsxRuntime.jsx(
9720
+ lucideReact.Search,
9721
+ {
9722
+ "aria-hidden": "true",
9723
+ className: "text-muted-foreground size-5",
9724
+ strokeWidth: 1.5
9725
+ }
9726
+ ),
9727
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-primary text-sm font-semibold", children: labels.noMatchesTitle }),
9728
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground text-[13px] leading-[1.4]", children: compact ? labels.flyoutNoMatches(query) : labels.noMatches(moduleLabel, query) })
9729
+ ]
9730
+ }
9731
+ );
9732
+ }
9733
+ function ModuleCard({
9734
+ module,
9735
+ tileSize,
9736
+ regionId,
9737
+ flyoutRegionId,
9738
+ bodyWidth,
9739
+ side,
9740
+ collapsed,
9741
+ open,
9742
+ flyoutOpen,
9743
+ anyFlyoutOpen,
9744
+ settling,
9745
+ tooltipOpen,
9746
+ onTooltipOpenChange,
9747
+ onFlyoutClose,
9748
+ list,
9749
+ query,
9750
+ onQueryChange,
9751
+ onHeaderClick,
9752
+ onSelect
9753
+ }) {
9754
+ const { activeItemId, reduced, labels, resolveIcon } = list;
9755
+ const hasActive = module.items.some((item) => item.id === activeItemId);
9756
+ const tileActive = collapsed && hasActive;
9757
+ const tileSelected = anyFlyoutOpen ? flyoutOpen : tileActive;
9758
+ const fillAfterCollapse = tileSelected && tileActive && settling && !reduced;
9759
+ const fillTransition = fillAfterCollapse ? `0ms linear ${MOTION_MS}ms` : `${FADE_MS}ms linear 0ms`;
9760
+ const iconFlipDelay = fillAfterCollapse ? MOTION_MS : FADE_MS / 2;
9761
+ const tilePadding = (tileSize - 2 - 20) / 2;
9762
+ const cardRef = react.useRef(null);
9763
+ const wasCollapsed = react.useRef(collapsed);
9764
+ react.useEffect(() => {
9765
+ const justCollapsed = collapsed && !wasCollapsed.current;
9766
+ wasCollapsed.current = collapsed;
9767
+ if (!justCollapsed || !hasActive || reduced) return;
9768
+ const animation2 = cardRef.current?.animate?.(BOUNCE_KEYFRAMES, {
9769
+ duration: BOUNCE_MS,
9770
+ delay: MOTION_MS,
9771
+ easing: "ease-out"
9772
+ });
9773
+ return () => animation2?.cancel();
9774
+ }, [collapsed, hasActive, reduced]);
9775
+ const items = filterItems(module.items, query);
9776
+ const isEmpty = module.items.length === 0;
9777
+ const emptyMessage = /* @__PURE__ */ jsxRuntime.jsx(
9778
+ "p",
9779
+ {
9780
+ "data-slot": "floating-sidebar-empty-module",
9781
+ className: "text-muted-foreground px-3 py-4 text-center text-[13px] leading-[1.4]",
9782
+ children: labels.emptyModule
9783
+ }
9784
+ );
9785
+ const tileRef = react.useRef(null);
9786
+ const closedFromOutside = react.useRef(false);
9787
+ const skipNextTooltip = react.useRef(false);
9788
+ return /* @__PURE__ */ jsxRuntime.jsx(Popover, { open: flyoutOpen, onOpenChange: (next) => !next && onFlyoutClose(), children: /* @__PURE__ */ jsxRuntime.jsxs(
9789
+ "div",
9790
+ {
9791
+ ref: cardRef,
9792
+ "data-slot": "floating-sidebar-module",
9793
+ "data-module-id": module.id,
9794
+ "data-state": open ? "open" : "closed",
9795
+ "data-active": hasActive ? "true" : void 0,
9796
+ className: cn(
9797
+ "relative flex min-h-0 w-full flex-col",
9798
+ "border",
9799
+ collapsed ? "rounded-[14px]" : "rounded-2xl",
9800
+ tileSelected ? "border-primary bg-primary" : "bg-sidebar border-floating-sidebar-card-border",
9801
+ open ? "shadow-[0_8px_24px_color-mix(in_srgb,var(--color-primary)_8%,transparent)]" : "shadow-[0_2px_8px_color-mix(in_srgb,var(--color-primary)_4%,transparent)]"
9802
+ ),
9803
+ style: {
9804
+ flex: open ? "0 1 auto" : "0 0 auto",
9805
+ transition: transition2(
9806
+ reduced,
9807
+ t("border-radius", MOTION_MS),
9808
+ `background-color ${fillTransition}`,
9809
+ `border-color ${fillTransition}`,
9810
+ t("box-shadow", MOTION_MS)
9811
+ )
9812
+ },
9813
+ children: [
9814
+ /* @__PURE__ */ jsxRuntime.jsxs(
9815
+ Tooltip,
9816
+ {
9817
+ open: tooltipOpen,
9818
+ onOpenChange: (next) => {
9819
+ if (next && skipNextTooltip.current) return;
9820
+ onTooltipOpenChange(collapsed && next);
9821
+ },
9822
+ children: [
9823
+ /* @__PURE__ */ jsxRuntime.jsx(PopoverAnchor, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsxs(
9824
+ "button",
9825
+ {
9826
+ ref: tileRef,
9827
+ type: "button",
9828
+ "data-slot": "floating-sidebar-module-trigger",
9829
+ "aria-label": collapsed && hasActive ? module.label + labels.currentPageSuffix : module.label,
9830
+ "aria-expanded": collapsed ? flyoutOpen : open,
9831
+ "aria-controls": collapsed ? flyoutOpen ? flyoutRegionId : void 0 : regionId,
9832
+ onClick: onHeaderClick,
9833
+ className: cn(
9834
+ "group/head focus-visible:ring-primary relative flex w-full shrink-0 cursor-pointer items-center gap-3 overflow-hidden border-0 bg-transparent text-left outline-none focus-visible:ring-2 focus-visible:ring-offset-2",
9835
+ collapsed ? "rounded-[14px]" : "h-[58px] rounded-2xl",
9836
+ tileSelected ? "text-primary-foreground" : "text-primary"
9837
+ ),
9838
+ style: {
9839
+ // Collapsed: a square tile (border included) with the icon centered.
9840
+ ...collapsed && { height: tileSize - 2 },
9841
+ paddingLeft: collapsed ? tilePadding : 20,
9842
+ paddingRight: collapsed ? tilePadding : 20,
9843
+ transition: transition2(
9844
+ reduced,
9845
+ t("height", MOTION_MS),
9846
+ t("padding", MOTION_MS),
9847
+ `color 0ms linear ${iconFlipDelay}ms`,
9848
+ t("border-radius", MOTION_MS)
9849
+ )
9850
+ },
9851
+ children: [
9852
+ /* @__PURE__ */ jsxRuntime.jsx(
9853
+ "span",
9854
+ {
9855
+ "aria-hidden": "true",
9856
+ "data-slot": "floating-sidebar-module-hover",
9857
+ className: cn(
9858
+ "pointer-events-none absolute transition-colors duration-180 ease-out",
9859
+ // Inset on expanded cards; edge to edge on collapsed tiles.
9860
+ collapsed ? "inset-0 rounded-[inherit]" : "inset-1.5 rounded-[10px]",
9861
+ !open && (tileSelected ? "group-hover/head:bg-primary-foreground/12" : "group-hover/head:bg-floating-sidebar-hover")
9862
+ )
9863
+ }
9864
+ ),
9865
+ /* @__PURE__ */ jsxRuntime.jsx(
9866
+ "span",
9867
+ {
9868
+ "data-slot": "floating-sidebar-module-icon",
9869
+ className: "relative flex shrink-0 items-center justify-center",
9870
+ children: /* @__PURE__ */ jsxRuntime.jsx(SidebarIcon, { icon: module.icon, resolveIcon })
9871
+ }
9872
+ ),
9873
+ /* @__PURE__ */ jsxRuntime.jsx(
9874
+ "span",
9875
+ {
9876
+ "data-slot": "floating-sidebar-module-label",
9877
+ "aria-hidden": collapsed || void 0,
9878
+ className: "relative min-w-0 flex-1 text-sm font-medium whitespace-nowrap",
9879
+ style: {
9880
+ opacity: collapsed ? 0 : 1,
9881
+ transition: transition2(
9882
+ reduced,
9883
+ t("opacity", FADE_MS, collapsed ? 0 : FADE_MS)
9884
+ )
9885
+ },
9886
+ children: module.label
9887
+ }
9888
+ ),
9889
+ /* @__PURE__ */ jsxRuntime.jsx(
9890
+ "span",
9891
+ {
9892
+ "data-slot": "floating-sidebar-module-chevron",
9893
+ "aria-hidden": "true",
9894
+ className: cn(
9895
+ "relative flex shrink-0",
9896
+ !tileSelected && "text-muted-foreground"
9897
+ ),
9898
+ style: {
9899
+ opacity: collapsed ? 0 : 1,
9900
+ transform: open ? "rotate(180deg)" : "rotate(0deg)",
9901
+ transition: transition2(
9902
+ reduced,
9903
+ t("transform", 220),
9904
+ t("opacity", FADE_MS, collapsed ? 0 : FADE_MS)
9905
+ )
9906
+ },
9907
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronDown, { className: "size-3.5", strokeWidth: 1.5 })
9908
+ }
9909
+ )
9910
+ ]
9911
+ }
9912
+ ) }) }),
9913
+ collapsed && /* @__PURE__ */ jsxRuntime.jsx(
9914
+ TooltipContent,
9915
+ {
9916
+ side: side === "right" ? "left" : "right",
9917
+ sideOffset: 12,
9918
+ "data-slot": "floating-sidebar-tooltip",
9919
+ children: module.label
9920
+ }
9921
+ )
9922
+ ]
9923
+ }
9924
+ ),
9925
+ /* @__PURE__ */ jsxRuntime.jsx(
9926
+ "div",
9927
+ {
9928
+ className: "grid min-h-0 flex-[1_1_auto]",
9929
+ style: {
9930
+ gridTemplateRows: open ? "1fr" : "0fr",
9931
+ transition: transition2(reduced, t("grid-template-rows", MOTION_MS))
9932
+ },
9933
+ children: /* @__PURE__ */ jsxRuntime.jsx(
9934
+ "div",
9935
+ {
9936
+ id: regionId,
9937
+ "data-slot": "floating-sidebar-module-content",
9938
+ "data-state": open ? "open" : "closed",
9939
+ "data-scroll": "muted",
9940
+ className: cn("min-h-0 overflow-x-hidden", SUBTLE_SCROLLBAR),
9941
+ style: {
9942
+ overflowY: open ? "auto" : "hidden",
9943
+ visibility: open ? "visible" : "hidden",
9944
+ transition: open ? "visibility 0s" : `visibility 0s linear ${MOTION_MS}ms`
9945
+ },
9946
+ children: /* @__PURE__ */ jsxRuntime.jsxs("div", { "data-slot": "floating-sidebar-module-body", style: { width: bodyWidth }, children: [
9947
+ isEmpty && emptyMessage,
9948
+ !isEmpty && /* @__PURE__ */ jsxRuntime.jsx(
9949
+ "div",
9950
+ {
9951
+ "data-slot": "floating-sidebar-search-bar",
9952
+ className: "bg-sidebar sticky top-0 z-[2] px-3 pb-3",
9953
+ children: /* @__PURE__ */ jsxRuntime.jsx(
9954
+ ModuleSearch,
9955
+ {
9956
+ moduleLabel: module.label,
9957
+ query,
9958
+ focusable: open,
9959
+ labels,
9960
+ onQueryChange
9961
+ }
9962
+ )
9963
+ }
9964
+ ),
9965
+ !isEmpty && /* @__PURE__ */ jsxRuntime.jsx(
9966
+ "ul",
9967
+ {
9968
+ "data-slot": "floating-sidebar-item-list",
9969
+ className: "flex flex-col gap-0.5 px-3 pb-3",
9970
+ children: items.map((item, index) => /* @__PURE__ */ jsxRuntime.jsx(
9971
+ ItemRow,
9972
+ {
9973
+ item,
9974
+ list,
9975
+ visible: open,
9976
+ staggerIndex: index,
9977
+ onSelect: (navigates) => onSelect(item, navigates)
9978
+ },
9979
+ item.id
9980
+ ))
9981
+ }
9982
+ ),
9983
+ !isEmpty && /* @__PURE__ */ jsxRuntime.jsx(
9984
+ NoMatches,
9985
+ {
9986
+ show: items.length === 0,
9987
+ moduleLabel: module.label,
9988
+ query,
9989
+ labels
9990
+ }
9991
+ )
9992
+ ] })
9993
+ }
9994
+ )
9995
+ }
9996
+ ),
9997
+ collapsed && /* @__PURE__ */ jsxRuntime.jsxs(
9998
+ PopoverContent,
9999
+ {
10000
+ id: flyoutRegionId,
10001
+ role: "group",
10002
+ "aria-label": module.label,
10003
+ "data-slot": "floating-sidebar-flyout",
10004
+ "data-side": side,
10005
+ side: side === "right" ? "left" : "right",
10006
+ align: "start",
10007
+ sideOffset: 16,
10008
+ collisionPadding: 8,
10009
+ className: "ring-floating-sidebar-card-border max-h-(--radix-popover-content-available-height) w-[268px] gap-0 rounded-2xl px-2 pt-4 pb-2",
10010
+ onEscapeKeyDown: (event) => {
10011
+ if (!query) return;
10012
+ event.preventDefault();
10013
+ onQueryChange("");
10014
+ },
10015
+ onInteractOutside: (event) => {
10016
+ if (tileRef.current?.contains(event.target)) {
10017
+ event.preventDefault();
10018
+ return;
10019
+ }
10020
+ closedFromOutside.current = true;
10021
+ },
10022
+ onCloseAutoFocus: (event) => {
10023
+ event.preventDefault();
10024
+ if (!closedFromOutside.current) {
10025
+ skipNextTooltip.current = true;
10026
+ tileRef.current?.focus();
10027
+ skipNextTooltip.current = false;
10028
+ }
10029
+ closedFromOutside.current = false;
10030
+ },
10031
+ children: [
10032
+ /* @__PURE__ */ jsxRuntime.jsx(
10033
+ "div",
10034
+ {
10035
+ "data-slot": "floating-sidebar-flyout-title",
10036
+ className: "text-muted-foreground px-3 pb-2.5 text-[11px] font-medium tracking-[.1em] uppercase",
10037
+ children: module.label
10038
+ }
10039
+ ),
10040
+ isEmpty && emptyMessage,
10041
+ !isEmpty && /* @__PURE__ */ jsxRuntime.jsx(
10042
+ ModuleSearch,
10043
+ {
10044
+ moduleLabel: module.label,
10045
+ query,
10046
+ focusable: flyoutOpen,
10047
+ labels,
10048
+ onQueryChange,
10049
+ className: "mx-2 mb-2"
10050
+ }
10051
+ ),
10052
+ /* @__PURE__ */ jsxRuntime.jsxs(
10053
+ "div",
10054
+ {
10055
+ "data-slot": "floating-sidebar-flyout-list",
10056
+ "data-scroll": "muted",
10057
+ className: cn(
10058
+ "max-h-[420px] min-h-0 overflow-y-auto",
10059
+ SUBTLE_SCROLLBAR
10060
+ ),
10061
+ children: [
10062
+ !isEmpty && /* @__PURE__ */ jsxRuntime.jsx(
10063
+ "ul",
10064
+ {
10065
+ "data-slot": "floating-sidebar-item-list",
10066
+ className: "flex flex-col gap-0.5",
10067
+ children: items.map((item) => /* @__PURE__ */ jsxRuntime.jsx(
10068
+ ItemRow,
10069
+ {
10070
+ item,
10071
+ list,
10072
+ visible: flyoutOpen,
10073
+ onSelect: (navigates) => onSelect(item, navigates)
10074
+ },
10075
+ item.id
10076
+ ))
10077
+ }
10078
+ ),
10079
+ !isEmpty && /* @__PURE__ */ jsxRuntime.jsx(
10080
+ NoMatches,
10081
+ {
10082
+ show: items.length === 0,
10083
+ moduleLabel: module.label,
10084
+ query,
10085
+ labels,
10086
+ compact: true
10087
+ }
10088
+ )
10089
+ ]
10090
+ }
10091
+ )
10092
+ ]
10093
+ }
10094
+ )
10095
+ ]
10096
+ }
10097
+ ) });
10098
+ }
10099
+ function ItemRow({ item, list, visible, staggerIndex, onSelect }) {
10100
+ const { activeItemId, favoriteIds, reduced, labels, resolveIcon, renderLink } = list;
10101
+ const active = item.id === activeItemId;
10102
+ const favorite = favoriteIds?.includes(item.id) ?? false;
10103
+ const tabIndex = visible ? 0 : -1;
10104
+ const staggered = staggerIndex !== void 0;
10105
+ const delay = staggered && visible ? Math.min(staggerIndex * 20, FADE_MS) : 0;
10106
+ const rowStyle = staggered ? {
10107
+ opacity: visible ? 1 : 0,
10108
+ transition: transition2(
10109
+ reduced,
10110
+ t("opacity", FADE_MS, delay),
10111
+ "background-color 180ms ease-out"
10112
+ )
10113
+ } : { transition: "background-color 180ms ease-out" };
10114
+ const className = cn(
10115
+ "focus-visible:ring-primary flex min-h-[46px] min-w-0 flex-1 cursor-pointer items-start gap-3 rounded-[10px] border-0 bg-transparent py-[13px] pr-1 pl-3 text-left text-[13px] leading-5 transition-transform duration-180 ease-[cubic-bezier(.2,.8,.2,1)] outline-none hover:text-primary focus-visible:ring-2 focus-visible:ring-inset disabled:cursor-not-allowed disabled:opacity-50",
10116
+ active ? "text-primary font-bold" : "font-normal text-floating-sidebar-item-foreground hover:translate-x-0.5 motion-reduce:hover:translate-x-0"
10117
+ );
10118
+ const content = /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
10119
+ /* @__PURE__ */ jsxRuntime.jsx(
10120
+ "span",
10121
+ {
10122
+ className: cn("flex shrink-0", active ? "text-primary" : "text-muted-foreground"),
10123
+ children: /* @__PURE__ */ jsxRuntime.jsx(SidebarIcon, { icon: item.icon, resolveIcon, small: true })
10124
+ }
10125
+ ),
10126
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "min-w-0 [overflow-wrap:anywhere]", children: item.label })
10127
+ ] });
10128
+ const common = {
10129
+ className,
10130
+ tabIndex,
10131
+ "aria-current": active ? "page" : void 0,
10132
+ "data-slot": "floating-sidebar-item",
10133
+ "data-active": active ? "true" : void 0
10134
+ };
10135
+ let control;
10136
+ if (item.href && !item.disabled) {
10137
+ const linkProps = {
10138
+ ...common,
10139
+ href: item.href,
10140
+ children: content,
10141
+ // Only a plain click on an in-app link moves the current page; external links and
10142
+ // modifier or middle clicks open elsewhere and leave this tab where it is.
10143
+ onClick: (event) => onSelect(
10144
+ !item.external && event.button === 0 && !(event.ctrlKey || event.metaKey || event.shiftKey || event.altKey)
10145
+ ),
10146
+ ...item.external ? { target: "_blank", rel: "noopener noreferrer" } : {}
10147
+ };
10148
+ control = renderLink ? renderLink(linkProps, item) : /* @__PURE__ */ jsxRuntime.jsx("a", { ...linkProps });
10149
+ } else {
10150
+ control = /* @__PURE__ */ jsxRuntime.jsx(
10151
+ "button",
10152
+ {
10153
+ type: "button",
10154
+ ...common,
10155
+ disabled: item.disabled,
10156
+ onClick: () => onSelect(false),
10157
+ children: content
10158
+ }
10159
+ );
10160
+ }
10161
+ const favoriteLabel = favorite ? labels.removeFavorite(item.label) : labels.addFavorite(item.label);
10162
+ return /* @__PURE__ */ jsxRuntime.jsxs(
10163
+ "li",
10164
+ {
10165
+ "data-slot": "floating-sidebar-item-row",
10166
+ className: cn(
10167
+ "group/row relative flex shrink-0 items-start rounded-[10px]",
10168
+ active ? "bg-accent/70" : "hover:bg-floating-sidebar-hover"
10169
+ ),
10170
+ style: rowStyle,
10171
+ children: [
10172
+ /* @__PURE__ */ jsxRuntime.jsx(
10173
+ "span",
10174
+ {
10175
+ "aria-hidden": "true",
10176
+ "data-slot": "floating-sidebar-item-indicator",
10177
+ className: "bg-floating-sidebar-indicator absolute top-3.5 bottom-3.5 left-0 w-[3px] rounded-r-[3px]",
10178
+ style: { opacity: active ? 1 : 0, transition: "opacity 160ms ease-out" }
10179
+ }
10180
+ ),
10181
+ control,
10182
+ favoriteIds && item.favoritable !== false && /* @__PURE__ */ jsxRuntime.jsx(
10183
+ "button",
10184
+ {
10185
+ type: "button",
10186
+ "data-slot": "floating-sidebar-favorite",
10187
+ "data-favorite": favorite ? "true" : void 0,
10188
+ "aria-pressed": favorite,
10189
+ "aria-label": favoriteLabel,
10190
+ title: favoriteLabel,
10191
+ tabIndex,
10192
+ onClick: (event) => {
10193
+ event.stopPropagation();
10194
+ if (!reduced) {
10195
+ event.currentTarget.animate?.(
10196
+ [
10197
+ { transform: "scale(1)" },
10198
+ { transform: "scale(1.3)" },
10199
+ { transform: "scale(1)" }
10200
+ ],
10201
+ { duration: 220, easing: "ease-out" }
10202
+ );
10203
+ }
10204
+ list.onToggleFavorite(item.id);
10205
+ },
10206
+ className: cn(
10207
+ "focus-visible:ring-primary hover:bg-primary/6 mt-[9px] mr-1.5 flex size-7 shrink-0 cursor-pointer items-center justify-center rounded-lg border-0 bg-transparent outline-none focus-visible:ring-2",
10208
+ "hover:text-secondary",
10209
+ // Stars stay hidden until their row is hovered or focused, unless favorited.
10210
+ favorite ? "text-secondary opacity-100" : (
10211
+ // Revealed on row hover or focus; always shown on touch, where there is no hover.
10212
+ "text-muted-foreground opacity-0 group-focus-within/row:opacity-100 group-hover/row:opacity-100 [@media(hover:none)]:opacity-100"
10213
+ )
10214
+ ),
10215
+ style: {
10216
+ transition: "color 120ms ease-out, background 120ms ease-out, opacity 120ms ease-out"
10217
+ },
10218
+ children: /* @__PURE__ */ jsxRuntime.jsx(
10219
+ lucideReact.Star,
10220
+ {
10221
+ "aria-hidden": "true",
10222
+ className: cn("size-3.5", favorite && "fill-secondary/10"),
10223
+ strokeWidth: 1.5
10224
+ }
10225
+ )
10226
+ }
10227
+ )
10228
+ ]
10229
+ }
10230
+ );
10231
+ }
9203
10232
  var DrawerContext = react.createContext({ isOpen: false, modal: true });
9204
10233
  function Drawer({ children, open, onOpenChange, modal = true, ...props }) {
9205
10234
  const [internalOpen, setInternalOpen] = react.useState(props.defaultOpen ?? false);
@@ -9576,8 +10605,9 @@ function SignaturePad({
9576
10605
  className: cn(
9577
10606
  // Fixed light "paper": the pen is black and the PNG is read on white by others. The
9578
10607
  // primitive keeps the pad light for consumers that define a dark theme, where a themed
9579
- // surface (`background`, `border`) would turn dark and hide the black ink.
9580
- "bg-main-alternative-secondary border-dark-100 relative overflow-hidden rounded-xl border",
10608
+ // surface (`background`, `border`) would turn dark and hide the black ink. The edge
10609
+ // uses `dark-400` (4.66:1 on white) so the pad reads as a control (WCAG 1.4.11).
10610
+ "bg-main-alternative-secondary border-dark-400 relative overflow-hidden rounded-xl border",
9581
10611
  "data-[disabled]:cursor-not-allowed data-[disabled]:opacity-50",
9582
10612
  className
9583
10613
  ),
@@ -9595,21 +10625,23 @@ function SignaturePad({
9595
10625
  className: "block w-full touch-none"
9596
10626
  }
9597
10627
  ),
9598
- /* @__PURE__ */ jsxRuntime.jsx(
10628
+ /* @__PURE__ */ jsxRuntime.jsxs(
9599
10629
  Button,
9600
10630
  {
9601
10631
  type: "button",
9602
10632
  variant: "ghost",
9603
- size: "icon-sm",
10633
+ size: "sm",
9604
10634
  "data-slot": "signature-pad-clear",
9605
- "aria-label": clearLabel,
9606
10635
  disabled,
9607
10636
  "aria-disabled": empty && !disabled ? true : void 0,
9608
10637
  onClick: () => {
9609
10638
  if (!emptyRef.current) clear();
9610
10639
  },
9611
- className: "bg-main-white text-dark-600 border-dark-100 hover:bg-dark-100 hover:text-main-black aria-disabled:hover:bg-main-white absolute right-2 bottom-2 border border-solid aria-disabled:cursor-not-allowed aria-disabled:opacity-50",
9612
- children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.BrushCleaning, { "aria-hidden": true, className: "size-4" })
10640
+ className: "bg-main-white text-dark-600 border-dark-400 hover:bg-dark-100 hover:text-main-black aria-disabled:hover:bg-main-white absolute right-2 bottom-2 h-11 max-w-[calc(100%-1rem)] gap-1.5 rounded-full border border-solid px-4 text-sm aria-disabled:cursor-not-allowed aria-disabled:opacity-70",
10641
+ children: [
10642
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.BrushCleaning, { "aria-hidden": true, className: "size-4 shrink-0" }),
10643
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "truncate", children: clearLabel })
10644
+ ]
9613
10645
  }
9614
10646
  )
9615
10647
  ]
@@ -9765,6 +10797,7 @@ exports.FieldError = FieldError;
9765
10797
  exports.FieldGroup = FieldGroup;
9766
10798
  exports.FieldLabel = FieldLabel;
9767
10799
  exports.FilterOption = FilterOption;
10800
+ exports.FloatingSidebar = FloatingSidebar;
9768
10801
  exports.Input = Input;
9769
10802
  exports.Item = Item;
9770
10803
  exports.ItemActions = ItemActions;