@bizapps/design-system-testing 1.10.1 → 1.12.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 +2373 -11
- package/dist/main.cjs.map +1 -1
- package/dist/main.css +1 -1
- package/dist/main.d.cts +356 -3
- package/dist/main.d.ts +356 -3
- package/dist/main.js +2373 -14
- package/dist/main.js.map +1 -1
- package/package.json +1 -1
package/dist/main.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import { SearchIcon, BellIcon, CircleHelpIcon, ChevronDownIcon, ChevronLeftIcon, MoreHorizontalIcon, ChevronRightIcon, MinusIcon, CheckIcon, LoaderCircle, ChevronUpIcon, X, ChevronLeft, ChevronRight, ExternalLinkIcon, ChevronsUpDownIcon, XIcon, UploadIcon, Loader2Icon, LoaderIcon, PipetteIcon, CalendarIcon, MenuIcon, Pencil, Clock, Funnel, ArrowUp, ArrowDown, ArrowUpDown, Ellipsis, Rows2, Eye, Search, BrushCleaning, House, Plane, Wallet, LayoutDashboardIcon, InfoIcon, TriangleAlertIcon, OctagonXIcon, CircleCheckIcon } from 'lucide-react';
|
|
2
|
+
import { SearchIcon, BellIcon, CircleHelpIcon, ChevronDownIcon, ChevronLeftIcon, MoreHorizontalIcon, ChevronRightIcon, MinusIcon, CheckIcon, LoaderCircle, ChevronUpIcon, X, ChevronLeft, ChevronRight, ExternalLinkIcon, ChevronsUpDownIcon, XIcon, UploadIcon, Loader2Icon, LoaderIcon, PipetteIcon, CalendarIcon, MenuIcon, Pencil, Clock, Funnel, ArrowUp, ArrowDown, ArrowUpDown, Ellipsis, Rows2, Eye, Search, ChevronsLeft, Menu, ChevronDown, Star, BrushCleaning, House, Plane, Wallet, LayoutDashboardIcon, CircleDashed, LayoutGrid, PanelRightClose, InfoIcon, TriangleAlertIcon, OctagonXIcon, CircleCheckIcon, ArrowRight, CircleAlert, ArrowUpRight, Lock } from 'lucide-react';
|
|
3
3
|
import { cva } from 'class-variance-authority';
|
|
4
4
|
import { clsx } from 'clsx';
|
|
5
5
|
import { twMerge } from 'tailwind-merge';
|
|
6
6
|
import { jsx, jsxs, Fragment as Fragment$1 } from 'react/jsx-runtime';
|
|
7
|
-
import { createContext, memo, useRef, useState, useMemo, useCallback, useEffect, forwardRef, Fragment, useId, useContext, useLayoutEffect, isValidElement, useImperativeHandle, cloneElement } from 'react';
|
|
7
|
+
import { createContext, memo, useRef, useState, useMemo, useCallback, useEffect, forwardRef, Fragment, useId, useContext, useLayoutEffect, isValidElement, useReducer, useImperativeHandle, cloneElement, useSyncExternalStore, use } from 'react';
|
|
8
8
|
import { Avatar as Avatar$1, Checkbox as Checkbox$1, Slot, Select as Select$1, Label as Label$1, Popover as Popover$1, Toggle as Toggle$1, ToggleGroup, Separator as Separator$1, Progress as Progress$1, Slider as Slider$1, Switch as Switch$1, Tabs as Tabs$1, Tooltip as Tooltip$1, Dialog as Dialog$1, AlertDialog as AlertDialog$1, Accordion as Accordion$1, DropdownMenu as DropdownMenu$1, RadioGroup as RadioGroup$1, NavigationMenu as NavigationMenu$1 } from 'radix-ui';
|
|
9
9
|
import { isSameDay, isToday, format, addDays } from 'date-fns';
|
|
10
10
|
import useEmblaCarousel from 'embla-carousel-react';
|
|
@@ -28,6 +28,7 @@ import { zodResolver } from '@hookform/resolvers/zod';
|
|
|
28
28
|
import confetti from 'canvas-confetti';
|
|
29
29
|
import { flexRender, useReactTable, getFilteredRowModel, getSortedRowModel, getCoreRowModel } from '@tanstack/react-table';
|
|
30
30
|
export { createColumnHelper } from '@tanstack/react-table';
|
|
31
|
+
import { createPortal } from 'react-dom';
|
|
31
32
|
import SignaturePadLib from 'signature_pad';
|
|
32
33
|
|
|
33
34
|
// src/components/ui/Alert/alert.tsx
|
|
@@ -2217,10 +2218,10 @@ function createStore() {
|
|
|
2217
2218
|
listeners: [],
|
|
2218
2219
|
add(item, id) {
|
|
2219
2220
|
const toastId = id ?? Math.random().toString(36).slice(2);
|
|
2220
|
-
const exists = store.toasts.find((
|
|
2221
|
+
const exists = store.toasts.find((t2) => t2.id === toastId);
|
|
2221
2222
|
if (exists) {
|
|
2222
2223
|
store.toasts = store.toasts.map(
|
|
2223
|
-
(
|
|
2224
|
+
(t2) => t2.id === toastId ? { ...t2, ...item, id: toastId } : t2
|
|
2224
2225
|
);
|
|
2225
2226
|
} else {
|
|
2226
2227
|
store.toasts = [{ id: toastId, ...item }, ...store.toasts].slice(0, MAX_TOASTS);
|
|
@@ -2235,7 +2236,7 @@ function createStore() {
|
|
|
2235
2236
|
remove(id) {
|
|
2236
2237
|
clearTimeout(timers.get(id));
|
|
2237
2238
|
timers.delete(id);
|
|
2238
|
-
store.toasts = store.toasts.filter((
|
|
2239
|
+
store.toasts = store.toasts.filter((t2) => t2.id !== id);
|
|
2239
2240
|
store.listeners.forEach((l) => l([...store.toasts]));
|
|
2240
2241
|
},
|
|
2241
2242
|
subscribe(listener) {
|
|
@@ -2379,7 +2380,7 @@ function Toaster({ position = "bottom-right", children }) {
|
|
|
2379
2380
|
"pointer-events-none fixed z-[100] flex flex-col gap-2",
|
|
2380
2381
|
positionStyles[position]
|
|
2381
2382
|
),
|
|
2382
|
-
children: /* @__PURE__ */ jsx(AnimatePresence, { mode: "sync", initial: false, children: toasts.map((
|
|
2383
|
+
children: /* @__PURE__ */ jsx(AnimatePresence, { mode: "sync", initial: false, children: toasts.map((t2) => /* @__PURE__ */ jsx("div", { className: "pointer-events-auto", children: /* @__PURE__ */ jsx(ToastCard, { ...t2 }) }, t2.id)) })
|
|
2383
2384
|
}
|
|
2384
2385
|
)
|
|
2385
2386
|
] });
|
|
@@ -5160,13 +5161,11 @@ function AbilityProvider({ children, permissions }) {
|
|
|
5160
5161
|
setImperativelyResolved(true);
|
|
5161
5162
|
}, []);
|
|
5162
5163
|
const permissionKey = permissions === void 0 ? null : [...permissions].sort().join("|");
|
|
5163
|
-
const syncedKey =
|
|
5164
|
-
|
|
5165
|
-
|
|
5166
|
-
if (permissionKey === syncedKey.current) return;
|
|
5167
|
-
syncedKey.current = permissionKey;
|
|
5164
|
+
const [syncedKey, setSyncedKey] = useState(permissionKey);
|
|
5165
|
+
if (permissionKey !== null && permissionKey !== syncedKey) {
|
|
5166
|
+
setSyncedKey(permissionKey);
|
|
5168
5167
|
setPermissionSet(new Set(permissions));
|
|
5169
|
-
}
|
|
5168
|
+
}
|
|
5170
5169
|
const value = useMemo(
|
|
5171
5170
|
() => ({ can: can2, setAbilities, clearAbilities, status }),
|
|
5172
5171
|
[can2, setAbilities, clearAbilities, status]
|
|
@@ -5180,6 +5179,9 @@ function useAbility() {
|
|
|
5180
5179
|
}
|
|
5181
5180
|
return context;
|
|
5182
5181
|
}
|
|
5182
|
+
function useOptionalAbility() {
|
|
5183
|
+
return useContext(AbilityContext);
|
|
5184
|
+
}
|
|
5183
5185
|
function useCan(permissions, mode = "any") {
|
|
5184
5186
|
const { can: can2 } = useAbility();
|
|
5185
5187
|
return can2(permissions, mode);
|
|
@@ -5403,10 +5405,11 @@ function SheetContent({
|
|
|
5403
5405
|
children,
|
|
5404
5406
|
side = "right",
|
|
5405
5407
|
showCloseButton = true,
|
|
5408
|
+
overlayClassName,
|
|
5406
5409
|
...props
|
|
5407
5410
|
}) {
|
|
5408
5411
|
return /* @__PURE__ */ jsxs(SheetPortal, { children: [
|
|
5409
|
-
/* @__PURE__ */ jsx(SheetOverlay, {}),
|
|
5412
|
+
/* @__PURE__ */ jsx(SheetOverlay, { className: overlayClassName }),
|
|
5410
5413
|
/* @__PURE__ */ jsxs(
|
|
5411
5414
|
Dialog$1.Content,
|
|
5412
5415
|
{
|
|
@@ -9174,6 +9177,2362 @@ function BottomNavTabsList({ items, className }) {
|
|
|
9174
9177
|
}
|
|
9175
9178
|
);
|
|
9176
9179
|
}
|
|
9180
|
+
var DEFAULT_LABELS = {
|
|
9181
|
+
navigation: "Modules navigation",
|
|
9182
|
+
title: "Modules",
|
|
9183
|
+
collapse: "Collapse menu",
|
|
9184
|
+
expand: "Expand menu",
|
|
9185
|
+
openMenu: "Open menu",
|
|
9186
|
+
closeMenu: "Close menu",
|
|
9187
|
+
currentPageSuffix: " (contains the current page)",
|
|
9188
|
+
searchPlaceholder: () => "Filter links\u2026",
|
|
9189
|
+
searchLabel: (moduleLabel) => `Filter links of ${moduleLabel}`,
|
|
9190
|
+
clearSearch: "Clear search",
|
|
9191
|
+
noMatchesTitle: "No matches",
|
|
9192
|
+
noMatches: (moduleLabel, query) => `No links in ${moduleLabel} match \u201C${query}\u201D.`,
|
|
9193
|
+
flyoutNoMatches: (query) => `No links match \u201C${query}\u201D.`,
|
|
9194
|
+
emptyModule: "No links in this module yet.",
|
|
9195
|
+
addFavorite: (itemLabel) => `Add to favorites: ${itemLabel}`,
|
|
9196
|
+
removeFavorite: (itemLabel) => `Remove from favorites: ${itemLabel}`
|
|
9197
|
+
};
|
|
9198
|
+
var EASE = "cubic-bezier(.2,.8,.2,1)";
|
|
9199
|
+
var MOTION_MS = 240;
|
|
9200
|
+
var FADE_MS = MOTION_MS / 2;
|
|
9201
|
+
var MORPH_MS = 360;
|
|
9202
|
+
var MORPH_EASE = "cubic-bezier(.65,0,.35,1)";
|
|
9203
|
+
var BOUNCE_KEYFRAMES = [
|
|
9204
|
+
{ transform: "translateY(0) scale(1)" },
|
|
9205
|
+
{ transform: "translateY(-3px) scale(1.06)", offset: 0.35 },
|
|
9206
|
+
{ transform: "translateY(0) scale(0.97)", offset: 0.65 },
|
|
9207
|
+
{ transform: "translateY(0) scale(1)" }
|
|
9208
|
+
];
|
|
9209
|
+
var BOUNCE_MS = 420;
|
|
9210
|
+
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!";
|
|
9211
|
+
var MORPH_SETTLE_MS = 120;
|
|
9212
|
+
function morphBox(element) {
|
|
9213
|
+
const rect = element.getBoundingClientRect();
|
|
9214
|
+
const computed = getComputedStyle(element);
|
|
9215
|
+
const drawer = element.closest('[data-slot="sheet-content"]');
|
|
9216
|
+
const drawerRect = drawer?.getBoundingClientRect();
|
|
9217
|
+
const offsetX = drawer && drawerRect ? drawerRect.left - drawer.offsetLeft : 0;
|
|
9218
|
+
const offsetY = drawer && drawerRect ? drawerRect.top - drawer.offsetTop : 0;
|
|
9219
|
+
const borderWidth = parseFloat(computed.borderTopWidth) || 0;
|
|
9220
|
+
return {
|
|
9221
|
+
left: rect.left - offsetX,
|
|
9222
|
+
top: rect.top - offsetY,
|
|
9223
|
+
width: rect.width,
|
|
9224
|
+
height: rect.height,
|
|
9225
|
+
// `rounded-full` resolves to an enormous radius that would not interpolate visibly;
|
|
9226
|
+
// half the height draws the same pill.
|
|
9227
|
+
borderRadius: `${Math.min(parseFloat(computed.borderTopLeftRadius) || 0, rect.height / 2)}px`,
|
|
9228
|
+
borderWidth: `${borderWidth}px`,
|
|
9229
|
+
borderColor: borderWidth > 0 ? computed.borderTopColor : "transparent",
|
|
9230
|
+
backgroundColor: computed.backgroundColor,
|
|
9231
|
+
boxShadow: computed.boxShadow
|
|
9232
|
+
};
|
|
9233
|
+
}
|
|
9234
|
+
var morphStyle = (b) => ({
|
|
9235
|
+
left: `${b.left}px`,
|
|
9236
|
+
top: `${b.top}px`,
|
|
9237
|
+
width: `${b.width}px`,
|
|
9238
|
+
height: `${b.height}px`,
|
|
9239
|
+
borderRadius: b.borderRadius,
|
|
9240
|
+
borderWidth: b.borderWidth,
|
|
9241
|
+
borderColor: b.borderColor,
|
|
9242
|
+
backgroundColor: b.backgroundColor,
|
|
9243
|
+
boxShadow: b.boxShadow
|
|
9244
|
+
});
|
|
9245
|
+
var sameBox = (a, b) => Math.abs(a.left - b.left) < 0.5 && Math.abs(a.top - b.top) < 0.5 && Math.abs(a.width - b.width) < 0.5 && Math.abs(a.height - b.height) < 0.5;
|
|
9246
|
+
function MobileMorph({ morph, side, ghostRef, getTarget, onDone }) {
|
|
9247
|
+
const chevronsRef = useRef(null);
|
|
9248
|
+
const menuRef = useRef(null);
|
|
9249
|
+
const onDoneRef = useRef(onDone);
|
|
9250
|
+
useLayoutEffect(() => {
|
|
9251
|
+
onDoneRef.current = onDone;
|
|
9252
|
+
});
|
|
9253
|
+
useLayoutEffect(() => {
|
|
9254
|
+
const ghost = ghostRef.current;
|
|
9255
|
+
if (!ghost) return;
|
|
9256
|
+
let frame = 0;
|
|
9257
|
+
let tries = 0;
|
|
9258
|
+
const animations = [];
|
|
9259
|
+
const options = {
|
|
9260
|
+
duration: MORPH_MS,
|
|
9261
|
+
easing: MORPH_EASE,
|
|
9262
|
+
fill: "forwards"
|
|
9263
|
+
};
|
|
9264
|
+
const start = () => {
|
|
9265
|
+
const target = getTarget();
|
|
9266
|
+
if (!target) {
|
|
9267
|
+
if (tries++ < 10) frame = requestAnimationFrame(start);
|
|
9268
|
+
else onDoneRef.current();
|
|
9269
|
+
return;
|
|
9270
|
+
}
|
|
9271
|
+
const to = morphBox(target);
|
|
9272
|
+
const chevrons = morph.to === "open" ? 1 : 0;
|
|
9273
|
+
animations.push(
|
|
9274
|
+
ghost.animate([morphStyle(morph.from), morphStyle(to)], options),
|
|
9275
|
+
chevronsRef.current.animate(
|
|
9276
|
+
[{ opacity: morph.chevrons }, { opacity: chevrons }],
|
|
9277
|
+
options
|
|
9278
|
+
),
|
|
9279
|
+
menuRef.current.animate(
|
|
9280
|
+
[{ opacity: 1 - morph.chevrons }, { opacity: 1 - chevrons }],
|
|
9281
|
+
options
|
|
9282
|
+
)
|
|
9283
|
+
);
|
|
9284
|
+
animations[0].onfinish = () => {
|
|
9285
|
+
const settled = getTarget();
|
|
9286
|
+
const end = settled && morphBox(settled);
|
|
9287
|
+
if (!end || sameBox(end, to)) return onDoneRef.current();
|
|
9288
|
+
const settle = ghost.animate([morphStyle(to), morphStyle(end)], {
|
|
9289
|
+
...options,
|
|
9290
|
+
duration: MORPH_SETTLE_MS
|
|
9291
|
+
});
|
|
9292
|
+
animations.push(settle);
|
|
9293
|
+
settle.onfinish = () => onDoneRef.current();
|
|
9294
|
+
};
|
|
9295
|
+
};
|
|
9296
|
+
start();
|
|
9297
|
+
return () => {
|
|
9298
|
+
cancelAnimationFrame(frame);
|
|
9299
|
+
animations.forEach((animation2) => animation2.cancel());
|
|
9300
|
+
};
|
|
9301
|
+
}, [morph, getTarget, ghostRef]);
|
|
9302
|
+
return createPortal(
|
|
9303
|
+
/* @__PURE__ */ jsxs(
|
|
9304
|
+
"div",
|
|
9305
|
+
{
|
|
9306
|
+
ref: ghostRef,
|
|
9307
|
+
"aria-hidden": "true",
|
|
9308
|
+
"data-slot": "floating-sidebar-morph",
|
|
9309
|
+
"data-state": morph.to,
|
|
9310
|
+
className: "text-primary pointer-events-none fixed z-[60] flex items-center justify-center overflow-hidden border-solid",
|
|
9311
|
+
style: morphStyle(morph.from),
|
|
9312
|
+
children: [
|
|
9313
|
+
/* @__PURE__ */ jsx(
|
|
9314
|
+
"span",
|
|
9315
|
+
{
|
|
9316
|
+
ref: chevronsRef,
|
|
9317
|
+
"data-slot": "floating-sidebar-morph-chevrons",
|
|
9318
|
+
className: "absolute flex",
|
|
9319
|
+
style: {
|
|
9320
|
+
opacity: morph.chevrons,
|
|
9321
|
+
// Same direction as the close button's chevrons in the drawer.
|
|
9322
|
+
transform: side === "right" ? "rotate(180deg)" : void 0
|
|
9323
|
+
},
|
|
9324
|
+
children: /* @__PURE__ */ jsx(ChevronsLeft, { className: "size-5", strokeWidth: 1.5 })
|
|
9325
|
+
}
|
|
9326
|
+
),
|
|
9327
|
+
/* @__PURE__ */ jsx("span", { ref: menuRef, className: "absolute flex", style: { opacity: 1 - morph.chevrons }, children: /* @__PURE__ */ jsx(Menu, { className: "size-5", strokeWidth: 1.5 }) })
|
|
9328
|
+
]
|
|
9329
|
+
}
|
|
9330
|
+
),
|
|
9331
|
+
document.body
|
|
9332
|
+
);
|
|
9333
|
+
}
|
|
9334
|
+
var DEFAULT_WIDTH = 305;
|
|
9335
|
+
var DEFAULT_COLLAPSED_WIDTH = 48;
|
|
9336
|
+
var TILE_ICON_SIZE = 22;
|
|
9337
|
+
var MIN_COLLAPSED_WIDTH = 40;
|
|
9338
|
+
var t = (prop, ms = 300, delay = 0) => `${prop} ${ms}ms ${EASE} ${delay}ms`;
|
|
9339
|
+
function transition2(reduced, ...parts) {
|
|
9340
|
+
const kept = reduced ? parts.filter((part) => /^(opacity|visibility|background|color|box-shadow)/.test(part)) : parts;
|
|
9341
|
+
return kept.join(", ") || "none";
|
|
9342
|
+
}
|
|
9343
|
+
function useControllableState2(value, defaultValue, onChange) {
|
|
9344
|
+
const [internal, setInternal] = useState(defaultValue);
|
|
9345
|
+
const isControlled = value !== void 0;
|
|
9346
|
+
const current = isControlled ? value : internal;
|
|
9347
|
+
const setValue = useCallback(
|
|
9348
|
+
(next) => {
|
|
9349
|
+
if (!isControlled) setInternal(next);
|
|
9350
|
+
onChange?.(next);
|
|
9351
|
+
},
|
|
9352
|
+
[isControlled, onChange]
|
|
9353
|
+
);
|
|
9354
|
+
return [current, setValue];
|
|
9355
|
+
}
|
|
9356
|
+
var normalize = (value) => value.normalize("NFD").replace(/[̀-ͯ]/g, "").toLowerCase();
|
|
9357
|
+
function filterItems(items, query) {
|
|
9358
|
+
const needle = normalize(query.trim());
|
|
9359
|
+
return needle ? items.filter((item) => normalize(item.label).includes(needle)) : items;
|
|
9360
|
+
}
|
|
9361
|
+
var lastUsedSidebar = null;
|
|
9362
|
+
var SHORTCUT_SIDEBAR = '[data-slot="floating-sidebar"][data-shortcut]';
|
|
9363
|
+
function isEditableTarget(target) {
|
|
9364
|
+
if (!(target instanceof HTMLElement)) return false;
|
|
9365
|
+
if (target.closest('[data-slot="floating-sidebar-search"]')) return false;
|
|
9366
|
+
return target.closest('[contenteditable]:not([contenteditable="false"])') !== null || target.tagName === "INPUT" || target.tagName === "TEXTAREA" || target.tagName === "SELECT";
|
|
9367
|
+
}
|
|
9368
|
+
var ICON_SIZES = {
|
|
9369
|
+
sm: "[&>svg]:size-[18px]",
|
|
9370
|
+
md: "[&>svg]:size-5",
|
|
9371
|
+
lg: "[&>svg]:size-[22px]"
|
|
9372
|
+
};
|
|
9373
|
+
function SidebarIcon({
|
|
9374
|
+
icon,
|
|
9375
|
+
resolveIcon,
|
|
9376
|
+
size = "md"
|
|
9377
|
+
}) {
|
|
9378
|
+
const resolved = typeof icon === "string" ? resolveIcon?.(icon) : icon;
|
|
9379
|
+
const sizeClass2 = cn(
|
|
9380
|
+
ICON_SIZES[size],
|
|
9381
|
+
"[&>svg]:transition-[width,height] [&>svg]:duration-240 [&>svg]:ease-[cubic-bezier(.2,.8,.2,1)] motion-reduce:[&>svg]:transition-none"
|
|
9382
|
+
);
|
|
9383
|
+
if (resolved === void 0 || resolved === null) {
|
|
9384
|
+
return /* @__PURE__ */ jsx("span", { "data-slot": "floating-sidebar-fallback-icon", className: cn("flex", sizeClass2), children: /* @__PURE__ */ jsx(CircleDashed, { "aria-hidden": "true", strokeWidth: 1.5 }) });
|
|
9385
|
+
}
|
|
9386
|
+
return /* @__PURE__ */ jsx(
|
|
9387
|
+
"span",
|
|
9388
|
+
{
|
|
9389
|
+
"data-slot": "floating-sidebar-icon",
|
|
9390
|
+
className: cn("flex [&>svg]:stroke-[1.5]", sizeClass2),
|
|
9391
|
+
children: resolved
|
|
9392
|
+
}
|
|
9393
|
+
);
|
|
9394
|
+
}
|
|
9395
|
+
function FloatingSidebar({
|
|
9396
|
+
modules,
|
|
9397
|
+
resolveIcon,
|
|
9398
|
+
renderLink,
|
|
9399
|
+
open: openProp,
|
|
9400
|
+
defaultOpen = true,
|
|
9401
|
+
onOpenChange,
|
|
9402
|
+
side = "left",
|
|
9403
|
+
width = DEFAULT_WIDTH,
|
|
9404
|
+
collapsedWidth = DEFAULT_COLLAPSED_WIDTH,
|
|
9405
|
+
type = "single",
|
|
9406
|
+
openModuleIds: openModulesProp,
|
|
9407
|
+
defaultOpenModuleIds = [],
|
|
9408
|
+
onOpenModulesChange,
|
|
9409
|
+
hideEmptyModules = true,
|
|
9410
|
+
activeItemId: activeItemProp,
|
|
9411
|
+
defaultActiveItemId = null,
|
|
9412
|
+
onItemSelect,
|
|
9413
|
+
favoriteIds: favoritesProp,
|
|
9414
|
+
defaultFavoriteIds = [],
|
|
9415
|
+
onFavoritesChange,
|
|
9416
|
+
showFavorites = true,
|
|
9417
|
+
loading = false,
|
|
9418
|
+
emptyState,
|
|
9419
|
+
mobileOpen: mobileOpenProp,
|
|
9420
|
+
defaultMobileOpen = false,
|
|
9421
|
+
onMobileOpenChange,
|
|
9422
|
+
shortcut = true,
|
|
9423
|
+
labels: labelsProp,
|
|
9424
|
+
className,
|
|
9425
|
+
ref,
|
|
9426
|
+
style,
|
|
9427
|
+
onPointerDownCapture,
|
|
9428
|
+
onFocusCapture,
|
|
9429
|
+
...navProps
|
|
9430
|
+
}) {
|
|
9431
|
+
const labels = { ...DEFAULT_LABELS, ...labelsProp };
|
|
9432
|
+
const reduced = useMediaQuery("(prefers-reduced-motion: reduce)");
|
|
9433
|
+
const isMobile = useIsMobile();
|
|
9434
|
+
const baseId = useId();
|
|
9435
|
+
const [open, setOpen] = useControllableState2(openProp, defaultOpen, onOpenChange);
|
|
9436
|
+
const [rawOpenModuleIds, setOpenModuleIds] = useControllableState2(
|
|
9437
|
+
openModulesProp,
|
|
9438
|
+
defaultOpenModuleIds,
|
|
9439
|
+
onOpenModulesChange
|
|
9440
|
+
);
|
|
9441
|
+
const railWidth = Math.max(MIN_COLLAPSED_WIDTH, collapsedWidth);
|
|
9442
|
+
const [activeItemId, setActiveItemId] = useControllableState2(
|
|
9443
|
+
activeItemProp,
|
|
9444
|
+
defaultActiveItemId
|
|
9445
|
+
);
|
|
9446
|
+
const [favoriteIds, setFavoriteIds] = useControllableState2(
|
|
9447
|
+
favoritesProp,
|
|
9448
|
+
defaultFavoriteIds,
|
|
9449
|
+
onFavoritesChange
|
|
9450
|
+
);
|
|
9451
|
+
const [mobileOpen, setMobileOpen] = useControllableState2(
|
|
9452
|
+
mobileOpenProp,
|
|
9453
|
+
defaultMobileOpen,
|
|
9454
|
+
onMobileOpenChange
|
|
9455
|
+
);
|
|
9456
|
+
const morphEnabled = isMobile && mobileOpenProp === void 0 && !reduced;
|
|
9457
|
+
const triggerRef = useRef(null);
|
|
9458
|
+
const toggleRef = useRef(null);
|
|
9459
|
+
const morphGhostRef = useRef(null);
|
|
9460
|
+
const deferredFocus = useRef(null);
|
|
9461
|
+
const [morph, setMorph] = useState(null);
|
|
9462
|
+
if (morph && !morphEnabled) setMorph(null);
|
|
9463
|
+
const prevMobileOpen = useRef(mobileOpen);
|
|
9464
|
+
useLayoutEffect(() => {
|
|
9465
|
+
if (prevMobileOpen.current === mobileOpen) return;
|
|
9466
|
+
prevMobileOpen.current = mobileOpen;
|
|
9467
|
+
const ghost = morphGhostRef.current;
|
|
9468
|
+
const source = ghost ?? (mobileOpen ? triggerRef.current : toggleRef.current);
|
|
9469
|
+
deferredFocus.current = null;
|
|
9470
|
+
if (!morphEnabled || !source || typeof source.animate !== "function") return;
|
|
9471
|
+
deferredFocus.current = mobileOpen ? "toggle" : "trigger";
|
|
9472
|
+
const chevrons = ghost?.querySelector(
|
|
9473
|
+
'[data-slot="floating-sidebar-morph-chevrons"]'
|
|
9474
|
+
);
|
|
9475
|
+
setMorph((current) => ({
|
|
9476
|
+
to: mobileOpen ? "open" : "closed",
|
|
9477
|
+
from: morphBox(source),
|
|
9478
|
+
chevrons: chevrons ? parseFloat(getComputedStyle(chevrons).opacity) : mobileOpen ? 0 : 1,
|
|
9479
|
+
key: (current?.key ?? 0) + 1
|
|
9480
|
+
}));
|
|
9481
|
+
}, [mobileOpen, morphEnabled]);
|
|
9482
|
+
const getMorphTarget = useCallback(
|
|
9483
|
+
() => morph?.to === "open" ? toggleRef.current : triggerRef.current,
|
|
9484
|
+
[morph?.to]
|
|
9485
|
+
);
|
|
9486
|
+
const endMorph = useCallback(() => setMorph(null), []);
|
|
9487
|
+
useLayoutEffect(() => {
|
|
9488
|
+
if (morph) return;
|
|
9489
|
+
const pending = deferredFocus.current;
|
|
9490
|
+
deferredFocus.current = null;
|
|
9491
|
+
const active = document.activeElement;
|
|
9492
|
+
if (pending === "toggle") {
|
|
9493
|
+
const toggle2 = toggleRef.current;
|
|
9494
|
+
if (toggle2 && active === toggle2.closest('[data-slot="sheet-content"]')) toggle2.focus();
|
|
9495
|
+
} else if (pending === "trigger" && (!active || active === document.body)) {
|
|
9496
|
+
triggerRef.current?.focus();
|
|
9497
|
+
}
|
|
9498
|
+
}, [morph]);
|
|
9499
|
+
const morphing = morphEnabled && morph !== null;
|
|
9500
|
+
const finePointer = useMediaQuery("(hover: hover) and (pointer: fine)");
|
|
9501
|
+
const [queries, setQueries] = useState({});
|
|
9502
|
+
const [flyoutId, setFlyoutId] = useState(null);
|
|
9503
|
+
const [hoverId, setHoverId] = useState(null);
|
|
9504
|
+
const collapsed = !isMobile && !open;
|
|
9505
|
+
const [prevCollapsed, setPrevCollapsed] = useState(collapsed);
|
|
9506
|
+
const [settling, setSettling] = useState(false);
|
|
9507
|
+
if (prevCollapsed !== collapsed) {
|
|
9508
|
+
setPrevCollapsed(collapsed);
|
|
9509
|
+
setSettling(collapsed);
|
|
9510
|
+
setFlyoutId(null);
|
|
9511
|
+
setHoverId(null);
|
|
9512
|
+
}
|
|
9513
|
+
useEffect(() => {
|
|
9514
|
+
if (!settling) return;
|
|
9515
|
+
const timer = setTimeout(() => setSettling(false), MOTION_MS);
|
|
9516
|
+
return () => clearTimeout(timer);
|
|
9517
|
+
}, [settling]);
|
|
9518
|
+
const toggle = useCallback(() => setOpen(!open), [open, setOpen]);
|
|
9519
|
+
const navRef = useRef(null);
|
|
9520
|
+
useEffect(() => {
|
|
9521
|
+
if (!shortcut) return;
|
|
9522
|
+
const onKeyDown = (event) => {
|
|
9523
|
+
if (event.defaultPrevented || isEditableTarget(event.target)) return;
|
|
9524
|
+
if ((event.ctrlKey || event.metaKey) && !event.shiftKey && !event.altKey && event.key.toLowerCase() === "b") {
|
|
9525
|
+
const owner = event.target instanceof Element ? event.target.closest(SHORTCUT_SIDEBAR) : null;
|
|
9526
|
+
const nav2 = navRef.current;
|
|
9527
|
+
if (owner && owner !== nav2) return;
|
|
9528
|
+
if (!owner) {
|
|
9529
|
+
const fallback = lastUsedSidebar?.isConnected && lastUsedSidebar.matches(SHORTCUT_SIDEBAR) ? lastUsedSidebar : document.querySelector(SHORTCUT_SIDEBAR);
|
|
9530
|
+
if (fallback && fallback !== nav2) return;
|
|
9531
|
+
}
|
|
9532
|
+
event.preventDefault();
|
|
9533
|
+
if (isMobile) setMobileOpen(!mobileOpen);
|
|
9534
|
+
else toggle();
|
|
9535
|
+
}
|
|
9536
|
+
};
|
|
9537
|
+
document.addEventListener("keydown", onKeyDown);
|
|
9538
|
+
return () => document.removeEventListener("keydown", onKeyDown);
|
|
9539
|
+
}, [shortcut, toggle, isMobile, mobileOpen, setMobileOpen]);
|
|
9540
|
+
const selectItem = (item, module, navigates) => {
|
|
9541
|
+
if (navigates) setActiveItemId(item.id);
|
|
9542
|
+
setFlyoutId(null);
|
|
9543
|
+
if (isMobile) setMobileOpen(false);
|
|
9544
|
+
onItemSelect?.(item, module);
|
|
9545
|
+
};
|
|
9546
|
+
const toggleFavorite = (itemId) => setFavoriteIds(
|
|
9547
|
+
favoriteIds.includes(itemId) ? favoriteIds.filter((id) => id !== itemId) : [...favoriteIds, itemId]
|
|
9548
|
+
);
|
|
9549
|
+
const list = {
|
|
9550
|
+
activeItemId,
|
|
9551
|
+
favoriteIds: showFavorites ? favoriteIds : null,
|
|
9552
|
+
reduced,
|
|
9553
|
+
labels,
|
|
9554
|
+
resolveIcon,
|
|
9555
|
+
renderLink,
|
|
9556
|
+
onToggleFavorite: toggleFavorite
|
|
9557
|
+
};
|
|
9558
|
+
const toggleLabel = isMobile ? labels.closeMenu : collapsed ? labels.expand : labels.collapse;
|
|
9559
|
+
const ability = useOptionalAbility();
|
|
9560
|
+
const usesPermissions = modules.some(
|
|
9561
|
+
(module) => module.permission || module.items.some((item) => item.permission)
|
|
9562
|
+
);
|
|
9563
|
+
const permissionsPending = usesPermissions && ability?.status === "loading";
|
|
9564
|
+
const isAllowed = (entry) => !entry.permission || ability?.status === "ready" && ability.can(entry.permission, entry.permissionMode);
|
|
9565
|
+
const authorizedModules = usesPermissions ? modules.filter(isAllowed).flatMap((module) => {
|
|
9566
|
+
const items = module.items.filter(isAllowed);
|
|
9567
|
+
return items.length > 0 || module.items.length === 0 ? [{ ...module, items }] : [];
|
|
9568
|
+
}) : modules;
|
|
9569
|
+
const showSkeletons = loading || permissionsPending;
|
|
9570
|
+
const visibleModules = hideEmptyModules ? authorizedModules.filter((module) => module.items.length > 0) : authorizedModules;
|
|
9571
|
+
const visibleModuleIds = new Set(visibleModules.map((module) => module.id));
|
|
9572
|
+
const openModuleIds = type === "single" ? rawOpenModuleIds.filter((id) => visibleModuleIds.has(id)).slice(0, 1) : rawOpenModuleIds;
|
|
9573
|
+
const toggleModule = (moduleId) => {
|
|
9574
|
+
const isOpen = openModuleIds.includes(moduleId);
|
|
9575
|
+
if (type === "multiple") {
|
|
9576
|
+
setOpenModuleIds(
|
|
9577
|
+
isOpen ? openModuleIds.filter((id) => id !== moduleId) : [...openModuleIds, moduleId]
|
|
9578
|
+
);
|
|
9579
|
+
} else {
|
|
9580
|
+
setOpenModuleIds(isOpen ? [] : [moduleId]);
|
|
9581
|
+
}
|
|
9582
|
+
};
|
|
9583
|
+
const nav = /* @__PURE__ */ jsxs(
|
|
9584
|
+
"nav",
|
|
9585
|
+
{
|
|
9586
|
+
...navProps,
|
|
9587
|
+
ref: (node) => {
|
|
9588
|
+
navRef.current = node;
|
|
9589
|
+
if (typeof ref === "function") return ref(node);
|
|
9590
|
+
if (ref) ref.current = node;
|
|
9591
|
+
},
|
|
9592
|
+
onPointerDownCapture: (event) => {
|
|
9593
|
+
lastUsedSidebar = event.currentTarget;
|
|
9594
|
+
onPointerDownCapture?.(event);
|
|
9595
|
+
},
|
|
9596
|
+
onFocusCapture: (event) => {
|
|
9597
|
+
lastUsedSidebar = event.currentTarget;
|
|
9598
|
+
onFocusCapture?.(event);
|
|
9599
|
+
},
|
|
9600
|
+
"data-slot": "floating-sidebar",
|
|
9601
|
+
"data-shortcut": shortcut ? "" : void 0,
|
|
9602
|
+
"data-state": collapsed ? "collapsed" : "expanded",
|
|
9603
|
+
"data-side": side,
|
|
9604
|
+
"aria-label": labels.navigation,
|
|
9605
|
+
"aria-busy": showSkeletons || void 0,
|
|
9606
|
+
className: cn(
|
|
9607
|
+
"flex h-full min-h-0 flex-col gap-3",
|
|
9608
|
+
// useIsMobile only resolves after mount; hide the inline layout on phones with CSS
|
|
9609
|
+
// so the first paint never shows it before the drawer takes over.
|
|
9610
|
+
!isMobile && "max-md:hidden",
|
|
9611
|
+
className
|
|
9612
|
+
),
|
|
9613
|
+
style: isMobile ? style : {
|
|
9614
|
+
...style,
|
|
9615
|
+
width: collapsed ? railWidth : width,
|
|
9616
|
+
transition: transition2(reduced, t("width", MOTION_MS))
|
|
9617
|
+
},
|
|
9618
|
+
children: [
|
|
9619
|
+
/* @__PURE__ */ jsxs(
|
|
9620
|
+
"div",
|
|
9621
|
+
{
|
|
9622
|
+
"data-slot": "floating-sidebar-header",
|
|
9623
|
+
className: cn(
|
|
9624
|
+
"flex h-10 shrink-0 items-center",
|
|
9625
|
+
side === "right" && "flex-row-reverse"
|
|
9626
|
+
),
|
|
9627
|
+
children: [
|
|
9628
|
+
/* @__PURE__ */ jsx(
|
|
9629
|
+
"span",
|
|
9630
|
+
{
|
|
9631
|
+
title: labels.title,
|
|
9632
|
+
className: "text-muted-foreground overflow-hidden pl-1.5 text-[11px] font-medium tracking-[.12em] text-ellipsis whitespace-nowrap uppercase",
|
|
9633
|
+
style: {
|
|
9634
|
+
opacity: collapsed ? 0 : 1,
|
|
9635
|
+
maxWidth: collapsed ? 0 : 200,
|
|
9636
|
+
transition: transition2(
|
|
9637
|
+
reduced,
|
|
9638
|
+
t("opacity", FADE_MS, collapsed ? 0 : FADE_MS),
|
|
9639
|
+
t("max-width", MOTION_MS)
|
|
9640
|
+
)
|
|
9641
|
+
},
|
|
9642
|
+
children: labels.title
|
|
9643
|
+
}
|
|
9644
|
+
),
|
|
9645
|
+
/* @__PURE__ */ jsx(
|
|
9646
|
+
"button",
|
|
9647
|
+
{
|
|
9648
|
+
ref: toggleRef,
|
|
9649
|
+
type: "button",
|
|
9650
|
+
"data-slot": "floating-sidebar-toggle",
|
|
9651
|
+
"aria-label": toggleLabel,
|
|
9652
|
+
"aria-expanded": isMobile ? void 0 : !collapsed,
|
|
9653
|
+
title: shortcut && (!isMobile || finePointer) ? `${toggleLabel} (Ctrl/\u2318 + B)` : toggleLabel,
|
|
9654
|
+
onClick: isMobile ? () => setMobileOpen(false) : toggle,
|
|
9655
|
+
className: cn(
|
|
9656
|
+
"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",
|
|
9657
|
+
side === "right" ? "mr-auto" : "ml-auto"
|
|
9658
|
+
),
|
|
9659
|
+
style: {
|
|
9660
|
+
// Hidden, and out of reach of a stray tap, while the mobile morph stands in
|
|
9661
|
+
// for it.
|
|
9662
|
+
opacity: morphing ? 0 : void 0,
|
|
9663
|
+
pointerEvents: morphing ? "none" : void 0,
|
|
9664
|
+
// The auto margin keeps the toggle on the edge while the width animates;
|
|
9665
|
+
// only the outer margin changes, to center it on the collapsed rail.
|
|
9666
|
+
// (`auto` cannot be interpolated, so animating it made the toggle jump.)
|
|
9667
|
+
[side === "right" ? "marginLeft" : "marginRight"]: collapsed ? (railWidth - 32) / 2 : 0,
|
|
9668
|
+
transition: transition2(
|
|
9669
|
+
reduced,
|
|
9670
|
+
t(side === "right" ? "margin-left" : "margin-right", MOTION_MS)
|
|
9671
|
+
)
|
|
9672
|
+
},
|
|
9673
|
+
children: /* @__PURE__ */ jsx(
|
|
9674
|
+
"span",
|
|
9675
|
+
{
|
|
9676
|
+
className: "flex",
|
|
9677
|
+
style: {
|
|
9678
|
+
// The chevrons point towards the edge when they collapse the sidebar.
|
|
9679
|
+
transform: collapsed !== (side === "right") ? "rotate(180deg)" : "rotate(0deg)",
|
|
9680
|
+
transition: transition2(reduced, t("transform", MOTION_MS))
|
|
9681
|
+
},
|
|
9682
|
+
children: /* @__PURE__ */ jsx(ChevronsLeft, { "aria-hidden": "true", className: "size-5", strokeWidth: 1.5 })
|
|
9683
|
+
}
|
|
9684
|
+
)
|
|
9685
|
+
}
|
|
9686
|
+
)
|
|
9687
|
+
]
|
|
9688
|
+
}
|
|
9689
|
+
),
|
|
9690
|
+
/* @__PURE__ */ jsxs(
|
|
9691
|
+
"div",
|
|
9692
|
+
{
|
|
9693
|
+
"data-slot": "floating-sidebar-modules",
|
|
9694
|
+
className: "flex min-h-0 flex-1 flex-col",
|
|
9695
|
+
style: {
|
|
9696
|
+
gap: collapsed ? 8 : 12,
|
|
9697
|
+
transition: transition2(reduced, t("gap", MOTION_MS))
|
|
9698
|
+
},
|
|
9699
|
+
children: [
|
|
9700
|
+
showSkeletons && Array.from({ length: 4 }, (_, index) => /* @__PURE__ */ jsx("div", { "data-slot": "floating-sidebar-skeleton", children: /* @__PURE__ */ jsx(
|
|
9701
|
+
Skeleton,
|
|
9702
|
+
{
|
|
9703
|
+
className: collapsed ? "rounded-[14px]" : "h-[58px] w-full rounded-2xl",
|
|
9704
|
+
style: collapsed ? { width: railWidth, height: railWidth } : void 0
|
|
9705
|
+
}
|
|
9706
|
+
) }, index)),
|
|
9707
|
+
!showSkeletons && visibleModules.length === 0 && emptyState,
|
|
9708
|
+
!showSkeletons && visibleModules.map((module) => /* @__PURE__ */ jsx(
|
|
9709
|
+
ModuleCard,
|
|
9710
|
+
{
|
|
9711
|
+
tileSize: railWidth,
|
|
9712
|
+
module,
|
|
9713
|
+
regionId: `${baseId}-${module.id}`,
|
|
9714
|
+
flyoutRegionId: `${baseId}-${module.id}-flyout`,
|
|
9715
|
+
bodyWidth: isMobile ? void 0 : width - 2,
|
|
9716
|
+
collapsed,
|
|
9717
|
+
side,
|
|
9718
|
+
open: !collapsed && openModuleIds.includes(module.id),
|
|
9719
|
+
flyoutOpen: collapsed && flyoutId === module.id,
|
|
9720
|
+
anyFlyoutOpen: collapsed && flyoutId !== null,
|
|
9721
|
+
settling,
|
|
9722
|
+
tooltipOpen: collapsed && hoverId === module.id && !flyoutId,
|
|
9723
|
+
onTooltipOpenChange: (next) => setHoverId(
|
|
9724
|
+
(current) => next ? module.id : current === module.id ? null : current
|
|
9725
|
+
),
|
|
9726
|
+
onFlyoutClose: () => setFlyoutId((current) => current === module.id ? null : current),
|
|
9727
|
+
list,
|
|
9728
|
+
query: queries[module.id] ?? "",
|
|
9729
|
+
onQueryChange: (query) => setQueries((current) => ({ ...current, [module.id]: query })),
|
|
9730
|
+
onHeaderClick: () => {
|
|
9731
|
+
if (collapsed) {
|
|
9732
|
+
setHoverId(null);
|
|
9733
|
+
setFlyoutId(flyoutId === module.id ? null : module.id);
|
|
9734
|
+
} else {
|
|
9735
|
+
toggleModule(module.id);
|
|
9736
|
+
}
|
|
9737
|
+
},
|
|
9738
|
+
onSelect: (item, navigates) => selectItem(item, module, navigates)
|
|
9739
|
+
},
|
|
9740
|
+
module.id
|
|
9741
|
+
))
|
|
9742
|
+
]
|
|
9743
|
+
}
|
|
9744
|
+
)
|
|
9745
|
+
]
|
|
9746
|
+
}
|
|
9747
|
+
);
|
|
9748
|
+
if (isMobile) {
|
|
9749
|
+
return /* @__PURE__ */ jsxs(TooltipProvider, { delayDuration: 300, children: [
|
|
9750
|
+
/* @__PURE__ */ jsxs(Sheet, { open: mobileOpen, onOpenChange: setMobileOpen, children: [
|
|
9751
|
+
mobileOpenProp === void 0 && /* @__PURE__ */ jsx(SheetTrigger, { asChild: true, children: /* @__PURE__ */ jsx(
|
|
9752
|
+
"button",
|
|
9753
|
+
{
|
|
9754
|
+
ref: triggerRef,
|
|
9755
|
+
type: "button",
|
|
9756
|
+
"data-slot": "floating-sidebar-mobile-trigger",
|
|
9757
|
+
"aria-label": labels.openMenu,
|
|
9758
|
+
style: {
|
|
9759
|
+
opacity: morphing ? 0 : void 0,
|
|
9760
|
+
pointerEvents: morphing ? "none" : void 0
|
|
9761
|
+
},
|
|
9762
|
+
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",
|
|
9763
|
+
children: /* @__PURE__ */ jsx(Menu, { "aria-hidden": "true", className: "size-5", strokeWidth: 1.5 })
|
|
9764
|
+
}
|
|
9765
|
+
) }),
|
|
9766
|
+
/* @__PURE__ */ jsxs(
|
|
9767
|
+
SheetContent,
|
|
9768
|
+
{
|
|
9769
|
+
side,
|
|
9770
|
+
"aria-describedby": void 0,
|
|
9771
|
+
showCloseButton: false,
|
|
9772
|
+
overlayClassName: "bg-primary/20",
|
|
9773
|
+
className: "bg-transparent p-3 pt-2 shadow-none data-[side=left]:border-r-0 data-[side=right]:border-l-0",
|
|
9774
|
+
style: { width: width + 24, maxWidth: "100%" },
|
|
9775
|
+
onOpenAutoFocus: (event) => {
|
|
9776
|
+
if (deferredFocus.current !== "toggle") return;
|
|
9777
|
+
event.preventDefault();
|
|
9778
|
+
event.currentTarget?.focus();
|
|
9779
|
+
},
|
|
9780
|
+
onCloseAutoFocus: (event) => {
|
|
9781
|
+
if (deferredFocus.current === "trigger") event.preventDefault();
|
|
9782
|
+
},
|
|
9783
|
+
onEscapeKeyDown: (event) => {
|
|
9784
|
+
const active = document.activeElement;
|
|
9785
|
+
if (active instanceof HTMLInputElement && active.value && active.closest('[data-slot="floating-sidebar-search"]')) {
|
|
9786
|
+
event.preventDefault();
|
|
9787
|
+
}
|
|
9788
|
+
},
|
|
9789
|
+
children: [
|
|
9790
|
+
/* @__PURE__ */ jsx(SheetTitle, { className: "sr-only", children: labels.title }),
|
|
9791
|
+
nav
|
|
9792
|
+
]
|
|
9793
|
+
}
|
|
9794
|
+
)
|
|
9795
|
+
] }),
|
|
9796
|
+
morphing && /* @__PURE__ */ jsx(
|
|
9797
|
+
MobileMorph,
|
|
9798
|
+
{
|
|
9799
|
+
morph,
|
|
9800
|
+
side,
|
|
9801
|
+
ghostRef: morphGhostRef,
|
|
9802
|
+
getTarget: getMorphTarget,
|
|
9803
|
+
onDone: endMorph
|
|
9804
|
+
},
|
|
9805
|
+
morph.key
|
|
9806
|
+
)
|
|
9807
|
+
] });
|
|
9808
|
+
}
|
|
9809
|
+
return /* @__PURE__ */ jsx(TooltipProvider, { delayDuration: 300, children: nav });
|
|
9810
|
+
}
|
|
9811
|
+
function ModuleSearch({
|
|
9812
|
+
moduleLabel,
|
|
9813
|
+
query,
|
|
9814
|
+
focusable,
|
|
9815
|
+
labels,
|
|
9816
|
+
onQueryChange,
|
|
9817
|
+
className
|
|
9818
|
+
}) {
|
|
9819
|
+
const tabIndex = focusable ? 0 : -1;
|
|
9820
|
+
const inputRef = useRef(null);
|
|
9821
|
+
return /* @__PURE__ */ jsxs(
|
|
9822
|
+
"div",
|
|
9823
|
+
{
|
|
9824
|
+
"data-slot": "floating-sidebar-search",
|
|
9825
|
+
className: cn(
|
|
9826
|
+
"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",
|
|
9827
|
+
className
|
|
9828
|
+
),
|
|
9829
|
+
children: [
|
|
9830
|
+
/* @__PURE__ */ jsx(Search, { "aria-hidden": "true", className: "size-4 shrink-0", strokeWidth: 1.5 }),
|
|
9831
|
+
/* @__PURE__ */ jsx(
|
|
9832
|
+
"input",
|
|
9833
|
+
{
|
|
9834
|
+
ref: inputRef,
|
|
9835
|
+
type: "search",
|
|
9836
|
+
"aria-label": labels.searchLabel(moduleLabel),
|
|
9837
|
+
placeholder: labels.searchPlaceholder(moduleLabel),
|
|
9838
|
+
tabIndex,
|
|
9839
|
+
value: query,
|
|
9840
|
+
onChange: (event) => onQueryChange(event.target.value),
|
|
9841
|
+
onKeyDown: (event) => {
|
|
9842
|
+
if (event.key === "Escape" && query) {
|
|
9843
|
+
event.stopPropagation();
|
|
9844
|
+
onQueryChange("");
|
|
9845
|
+
}
|
|
9846
|
+
},
|
|
9847
|
+
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"
|
|
9848
|
+
}
|
|
9849
|
+
),
|
|
9850
|
+
query && /* @__PURE__ */ jsx(
|
|
9851
|
+
"button",
|
|
9852
|
+
{
|
|
9853
|
+
type: "button",
|
|
9854
|
+
"aria-label": labels.clearSearch,
|
|
9855
|
+
tabIndex,
|
|
9856
|
+
onClick: () => {
|
|
9857
|
+
onQueryChange("");
|
|
9858
|
+
inputRef.current?.focus();
|
|
9859
|
+
},
|
|
9860
|
+
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",
|
|
9861
|
+
children: /* @__PURE__ */ jsx(X, { "aria-hidden": "true", className: "size-4", strokeWidth: 1.5 })
|
|
9862
|
+
}
|
|
9863
|
+
)
|
|
9864
|
+
]
|
|
9865
|
+
}
|
|
9866
|
+
);
|
|
9867
|
+
}
|
|
9868
|
+
function NoMatches({
|
|
9869
|
+
show,
|
|
9870
|
+
moduleLabel,
|
|
9871
|
+
query,
|
|
9872
|
+
labels,
|
|
9873
|
+
compact
|
|
9874
|
+
}) {
|
|
9875
|
+
return /* @__PURE__ */ jsx("div", { role: "status", "data-slot": "floating-sidebar-no-matches", children: show && /* @__PURE__ */ jsx(
|
|
9876
|
+
NoMatchesMessage,
|
|
9877
|
+
{
|
|
9878
|
+
moduleLabel,
|
|
9879
|
+
query,
|
|
9880
|
+
labels,
|
|
9881
|
+
compact
|
|
9882
|
+
}
|
|
9883
|
+
) });
|
|
9884
|
+
}
|
|
9885
|
+
function NoMatchesMessage({
|
|
9886
|
+
moduleLabel,
|
|
9887
|
+
query,
|
|
9888
|
+
labels,
|
|
9889
|
+
compact = false
|
|
9890
|
+
}) {
|
|
9891
|
+
return /* @__PURE__ */ jsxs(
|
|
9892
|
+
"div",
|
|
9893
|
+
{
|
|
9894
|
+
className: cn(
|
|
9895
|
+
"flex flex-col items-center text-center",
|
|
9896
|
+
compact ? "gap-1 px-3 py-4" : "gap-1.5 px-3 py-5"
|
|
9897
|
+
),
|
|
9898
|
+
children: [
|
|
9899
|
+
!compact && /* @__PURE__ */ jsx(
|
|
9900
|
+
Search,
|
|
9901
|
+
{
|
|
9902
|
+
"aria-hidden": "true",
|
|
9903
|
+
className: "text-muted-foreground size-5",
|
|
9904
|
+
strokeWidth: 1.5
|
|
9905
|
+
}
|
|
9906
|
+
),
|
|
9907
|
+
/* @__PURE__ */ jsx("span", { className: "text-primary text-sm font-semibold", children: labels.noMatchesTitle }),
|
|
9908
|
+
/* @__PURE__ */ jsx("span", { className: "text-muted-foreground text-[13px] leading-[1.4]", children: compact ? labels.flyoutNoMatches(query) : labels.noMatches(moduleLabel, query) })
|
|
9909
|
+
]
|
|
9910
|
+
}
|
|
9911
|
+
);
|
|
9912
|
+
}
|
|
9913
|
+
function ModuleCard({
|
|
9914
|
+
module,
|
|
9915
|
+
tileSize,
|
|
9916
|
+
regionId,
|
|
9917
|
+
flyoutRegionId,
|
|
9918
|
+
bodyWidth,
|
|
9919
|
+
side,
|
|
9920
|
+
collapsed,
|
|
9921
|
+
open,
|
|
9922
|
+
flyoutOpen,
|
|
9923
|
+
anyFlyoutOpen,
|
|
9924
|
+
settling,
|
|
9925
|
+
tooltipOpen,
|
|
9926
|
+
onTooltipOpenChange,
|
|
9927
|
+
onFlyoutClose,
|
|
9928
|
+
list,
|
|
9929
|
+
query,
|
|
9930
|
+
onQueryChange,
|
|
9931
|
+
onHeaderClick,
|
|
9932
|
+
onSelect
|
|
9933
|
+
}) {
|
|
9934
|
+
const { activeItemId, reduced, labels, resolveIcon } = list;
|
|
9935
|
+
const hasActive = module.items.some((item) => item.id === activeItemId);
|
|
9936
|
+
const tileActive = collapsed && hasActive;
|
|
9937
|
+
const tileSelected = anyFlyoutOpen ? flyoutOpen : tileActive;
|
|
9938
|
+
const fillAfterCollapse = tileSelected && tileActive && settling && !reduced;
|
|
9939
|
+
const fillTransition = fillAfterCollapse ? `0ms linear ${MOTION_MS}ms` : `${FADE_MS}ms linear 0ms`;
|
|
9940
|
+
const iconFlipDelay = fillAfterCollapse ? MOTION_MS : FADE_MS / 2;
|
|
9941
|
+
const tilePadding = (tileSize - 2 - TILE_ICON_SIZE) / 2;
|
|
9942
|
+
const iconHighlighted = collapsed ? tileSelected : open;
|
|
9943
|
+
const cardRef = useRef(null);
|
|
9944
|
+
const wasCollapsed = useRef(collapsed);
|
|
9945
|
+
useEffect(() => {
|
|
9946
|
+
const justCollapsed = collapsed && !wasCollapsed.current;
|
|
9947
|
+
wasCollapsed.current = collapsed;
|
|
9948
|
+
if (!justCollapsed || !hasActive || reduced) return;
|
|
9949
|
+
const animation2 = cardRef.current?.animate?.(BOUNCE_KEYFRAMES, {
|
|
9950
|
+
duration: BOUNCE_MS,
|
|
9951
|
+
delay: MOTION_MS,
|
|
9952
|
+
easing: "ease-out"
|
|
9953
|
+
});
|
|
9954
|
+
return () => animation2?.cancel();
|
|
9955
|
+
}, [collapsed, hasActive, reduced]);
|
|
9956
|
+
const items = filterItems(module.items, query);
|
|
9957
|
+
const isEmpty = module.items.length === 0;
|
|
9958
|
+
const emptyMessage = /* @__PURE__ */ jsx(
|
|
9959
|
+
"p",
|
|
9960
|
+
{
|
|
9961
|
+
"data-slot": "floating-sidebar-empty-module",
|
|
9962
|
+
className: "text-muted-foreground px-3 py-4 text-center text-[13px] leading-[1.4]",
|
|
9963
|
+
children: labels.emptyModule
|
|
9964
|
+
}
|
|
9965
|
+
);
|
|
9966
|
+
const tileRef = useRef(null);
|
|
9967
|
+
const closedFromOutside = useRef(false);
|
|
9968
|
+
const skipNextTooltip = useRef(false);
|
|
9969
|
+
return /* @__PURE__ */ jsx(Popover, { open: flyoutOpen, onOpenChange: (next) => !next && onFlyoutClose(), children: /* @__PURE__ */ jsxs(
|
|
9970
|
+
"div",
|
|
9971
|
+
{
|
|
9972
|
+
ref: cardRef,
|
|
9973
|
+
"data-slot": "floating-sidebar-module",
|
|
9974
|
+
"data-module-id": module.id,
|
|
9975
|
+
"data-state": open ? "open" : "closed",
|
|
9976
|
+
"data-active": hasActive ? "true" : void 0,
|
|
9977
|
+
className: cn(
|
|
9978
|
+
"relative flex min-h-0 w-full flex-col",
|
|
9979
|
+
"border",
|
|
9980
|
+
collapsed ? "rounded-[14px]" : "rounded-2xl",
|
|
9981
|
+
tileSelected ? "border-primary bg-primary" : "bg-sidebar border-floating-sidebar-card-border",
|
|
9982
|
+
open ? "shadow-[0_4px_14px_color-mix(in_srgb,var(--color-primary)_3%,transparent)]" : "shadow-[0_1px_4px_color-mix(in_srgb,var(--color-primary)_3%,transparent)]"
|
|
9983
|
+
),
|
|
9984
|
+
style: {
|
|
9985
|
+
flex: open ? "0 1 auto" : "0 0 auto",
|
|
9986
|
+
transition: transition2(
|
|
9987
|
+
reduced,
|
|
9988
|
+
t("border-radius", MOTION_MS),
|
|
9989
|
+
`background-color ${fillTransition}`,
|
|
9990
|
+
`border-color ${fillTransition}`,
|
|
9991
|
+
t("box-shadow", MOTION_MS)
|
|
9992
|
+
)
|
|
9993
|
+
},
|
|
9994
|
+
children: [
|
|
9995
|
+
/* @__PURE__ */ jsxs(
|
|
9996
|
+
Tooltip,
|
|
9997
|
+
{
|
|
9998
|
+
open: tooltipOpen,
|
|
9999
|
+
onOpenChange: (next) => {
|
|
10000
|
+
if (next && skipNextTooltip.current) return;
|
|
10001
|
+
onTooltipOpenChange(collapsed && next);
|
|
10002
|
+
},
|
|
10003
|
+
children: [
|
|
10004
|
+
/* @__PURE__ */ jsx(PopoverAnchor, { asChild: true, children: /* @__PURE__ */ jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsxs(
|
|
10005
|
+
"button",
|
|
10006
|
+
{
|
|
10007
|
+
ref: tileRef,
|
|
10008
|
+
type: "button",
|
|
10009
|
+
"data-slot": "floating-sidebar-module-trigger",
|
|
10010
|
+
"aria-label": collapsed && hasActive ? module.label + labels.currentPageSuffix : module.label,
|
|
10011
|
+
"aria-expanded": collapsed ? flyoutOpen : open,
|
|
10012
|
+
"aria-controls": collapsed ? flyoutOpen ? flyoutRegionId : void 0 : regionId,
|
|
10013
|
+
onClick: onHeaderClick,
|
|
10014
|
+
className: cn(
|
|
10015
|
+
"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",
|
|
10016
|
+
collapsed ? "rounded-[14px]" : "h-[58px] rounded-2xl",
|
|
10017
|
+
tileSelected ? "text-primary-foreground" : "text-primary"
|
|
10018
|
+
),
|
|
10019
|
+
style: {
|
|
10020
|
+
// Collapsed: a square tile (border included) with the icon centered.
|
|
10021
|
+
...collapsed && { height: tileSize - 2 },
|
|
10022
|
+
paddingLeft: collapsed ? tilePadding : 20,
|
|
10023
|
+
paddingRight: collapsed ? tilePadding : 20,
|
|
10024
|
+
transition: transition2(
|
|
10025
|
+
reduced,
|
|
10026
|
+
t("height", MOTION_MS),
|
|
10027
|
+
t("padding", MOTION_MS),
|
|
10028
|
+
`color 0ms linear ${iconFlipDelay}ms`,
|
|
10029
|
+
t("border-radius", MOTION_MS)
|
|
10030
|
+
)
|
|
10031
|
+
},
|
|
10032
|
+
children: [
|
|
10033
|
+
/* @__PURE__ */ jsx(
|
|
10034
|
+
"span",
|
|
10035
|
+
{
|
|
10036
|
+
"aria-hidden": "true",
|
|
10037
|
+
"data-slot": "floating-sidebar-module-hover",
|
|
10038
|
+
className: cn(
|
|
10039
|
+
"pointer-events-none absolute transition-colors duration-180 ease-out",
|
|
10040
|
+
// Inset on expanded cards; edge to edge on collapsed tiles.
|
|
10041
|
+
collapsed ? "inset-0 rounded-[inherit]" : "inset-1.5 rounded-[10px]",
|
|
10042
|
+
!open && (tileSelected ? "group-hover/head:bg-primary-foreground/12" : "group-hover/head:bg-floating-sidebar-hover")
|
|
10043
|
+
)
|
|
10044
|
+
}
|
|
10045
|
+
),
|
|
10046
|
+
/* @__PURE__ */ jsx(
|
|
10047
|
+
"span",
|
|
10048
|
+
{
|
|
10049
|
+
"data-slot": "floating-sidebar-module-icon",
|
|
10050
|
+
className: cn(
|
|
10051
|
+
"relative flex shrink-0 items-center justify-center",
|
|
10052
|
+
!iconHighlighted && "text-muted-foreground"
|
|
10053
|
+
),
|
|
10054
|
+
style: {
|
|
10055
|
+
transition: transition2(
|
|
10056
|
+
reduced,
|
|
10057
|
+
`color 0ms linear ${iconFlipDelay}ms`
|
|
10058
|
+
)
|
|
10059
|
+
},
|
|
10060
|
+
children: /* @__PURE__ */ jsx(
|
|
10061
|
+
SidebarIcon,
|
|
10062
|
+
{
|
|
10063
|
+
icon: module.icon,
|
|
10064
|
+
resolveIcon,
|
|
10065
|
+
size: collapsed ? "lg" : "md"
|
|
10066
|
+
}
|
|
10067
|
+
)
|
|
10068
|
+
}
|
|
10069
|
+
),
|
|
10070
|
+
/* @__PURE__ */ jsx(
|
|
10071
|
+
"span",
|
|
10072
|
+
{
|
|
10073
|
+
"data-slot": "floating-sidebar-module-label",
|
|
10074
|
+
"aria-hidden": collapsed || void 0,
|
|
10075
|
+
className: "relative min-w-0 flex-1 text-sm font-medium whitespace-nowrap",
|
|
10076
|
+
style: {
|
|
10077
|
+
opacity: collapsed ? 0 : 1,
|
|
10078
|
+
transition: transition2(
|
|
10079
|
+
reduced,
|
|
10080
|
+
t("opacity", FADE_MS, collapsed ? 0 : FADE_MS)
|
|
10081
|
+
)
|
|
10082
|
+
},
|
|
10083
|
+
children: module.label
|
|
10084
|
+
}
|
|
10085
|
+
),
|
|
10086
|
+
/* @__PURE__ */ jsx(
|
|
10087
|
+
"span",
|
|
10088
|
+
{
|
|
10089
|
+
"data-slot": "floating-sidebar-module-chevron",
|
|
10090
|
+
"aria-hidden": "true",
|
|
10091
|
+
className: cn(
|
|
10092
|
+
"relative flex shrink-0",
|
|
10093
|
+
!tileSelected && "text-muted-foreground"
|
|
10094
|
+
),
|
|
10095
|
+
style: {
|
|
10096
|
+
opacity: collapsed ? 0 : 1,
|
|
10097
|
+
transform: open ? "rotate(180deg)" : "rotate(0deg)",
|
|
10098
|
+
transition: transition2(
|
|
10099
|
+
reduced,
|
|
10100
|
+
t("transform", 220),
|
|
10101
|
+
t("opacity", FADE_MS, collapsed ? 0 : FADE_MS)
|
|
10102
|
+
)
|
|
10103
|
+
},
|
|
10104
|
+
children: /* @__PURE__ */ jsx(ChevronDown, { className: "size-3.5", strokeWidth: 1.5 })
|
|
10105
|
+
}
|
|
10106
|
+
)
|
|
10107
|
+
]
|
|
10108
|
+
}
|
|
10109
|
+
) }) }),
|
|
10110
|
+
collapsed && /* @__PURE__ */ jsx(
|
|
10111
|
+
TooltipContent,
|
|
10112
|
+
{
|
|
10113
|
+
side: side === "right" ? "left" : "right",
|
|
10114
|
+
sideOffset: 12,
|
|
10115
|
+
"data-slot": "floating-sidebar-tooltip",
|
|
10116
|
+
children: module.label
|
|
10117
|
+
}
|
|
10118
|
+
)
|
|
10119
|
+
]
|
|
10120
|
+
}
|
|
10121
|
+
),
|
|
10122
|
+
/* @__PURE__ */ jsx(
|
|
10123
|
+
"div",
|
|
10124
|
+
{
|
|
10125
|
+
className: "grid min-h-0 flex-[1_1_auto]",
|
|
10126
|
+
style: {
|
|
10127
|
+
gridTemplateRows: open ? "1fr" : "0fr",
|
|
10128
|
+
transition: transition2(reduced, t("grid-template-rows", MOTION_MS))
|
|
10129
|
+
},
|
|
10130
|
+
children: /* @__PURE__ */ jsx(
|
|
10131
|
+
"div",
|
|
10132
|
+
{
|
|
10133
|
+
id: regionId,
|
|
10134
|
+
"data-slot": "floating-sidebar-module-content",
|
|
10135
|
+
"data-state": open ? "open" : "closed",
|
|
10136
|
+
"data-scroll": "muted",
|
|
10137
|
+
className: cn("min-h-0 overflow-x-hidden", SUBTLE_SCROLLBAR),
|
|
10138
|
+
style: {
|
|
10139
|
+
overflowY: open ? "auto" : "hidden",
|
|
10140
|
+
visibility: open ? "visible" : "hidden",
|
|
10141
|
+
transition: open ? "visibility 0s" : `visibility 0s linear ${MOTION_MS}ms`
|
|
10142
|
+
},
|
|
10143
|
+
children: /* @__PURE__ */ jsxs("div", { "data-slot": "floating-sidebar-module-body", style: { width: bodyWidth }, children: [
|
|
10144
|
+
isEmpty && emptyMessage,
|
|
10145
|
+
!isEmpty && /* @__PURE__ */ jsx(
|
|
10146
|
+
"div",
|
|
10147
|
+
{
|
|
10148
|
+
"data-slot": "floating-sidebar-search-bar",
|
|
10149
|
+
className: "bg-sidebar sticky top-0 z-[2] px-3 pb-3",
|
|
10150
|
+
children: /* @__PURE__ */ jsx(
|
|
10151
|
+
ModuleSearch,
|
|
10152
|
+
{
|
|
10153
|
+
moduleLabel: module.label,
|
|
10154
|
+
query,
|
|
10155
|
+
focusable: open,
|
|
10156
|
+
labels,
|
|
10157
|
+
onQueryChange
|
|
10158
|
+
}
|
|
10159
|
+
)
|
|
10160
|
+
}
|
|
10161
|
+
),
|
|
10162
|
+
!isEmpty && /* @__PURE__ */ jsx(
|
|
10163
|
+
"ul",
|
|
10164
|
+
{
|
|
10165
|
+
"data-slot": "floating-sidebar-item-list",
|
|
10166
|
+
className: "flex flex-col gap-0.5 px-3 pb-3",
|
|
10167
|
+
children: items.map((item, index) => /* @__PURE__ */ jsx(
|
|
10168
|
+
ItemRow,
|
|
10169
|
+
{
|
|
10170
|
+
item,
|
|
10171
|
+
list,
|
|
10172
|
+
visible: open,
|
|
10173
|
+
staggerIndex: index,
|
|
10174
|
+
onSelect: (navigates) => onSelect(item, navigates)
|
|
10175
|
+
},
|
|
10176
|
+
item.id
|
|
10177
|
+
))
|
|
10178
|
+
}
|
|
10179
|
+
),
|
|
10180
|
+
!isEmpty && /* @__PURE__ */ jsx(
|
|
10181
|
+
NoMatches,
|
|
10182
|
+
{
|
|
10183
|
+
show: items.length === 0,
|
|
10184
|
+
moduleLabel: module.label,
|
|
10185
|
+
query,
|
|
10186
|
+
labels
|
|
10187
|
+
}
|
|
10188
|
+
)
|
|
10189
|
+
] })
|
|
10190
|
+
}
|
|
10191
|
+
)
|
|
10192
|
+
}
|
|
10193
|
+
),
|
|
10194
|
+
collapsed && /* @__PURE__ */ jsxs(
|
|
10195
|
+
PopoverContent,
|
|
10196
|
+
{
|
|
10197
|
+
id: flyoutRegionId,
|
|
10198
|
+
role: "group",
|
|
10199
|
+
"aria-label": module.label,
|
|
10200
|
+
"data-slot": "floating-sidebar-flyout",
|
|
10201
|
+
"data-side": side,
|
|
10202
|
+
side: side === "right" ? "left" : "right",
|
|
10203
|
+
align: "start",
|
|
10204
|
+
sideOffset: 16,
|
|
10205
|
+
collisionPadding: 8,
|
|
10206
|
+
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",
|
|
10207
|
+
onEscapeKeyDown: (event) => {
|
|
10208
|
+
if (!query) return;
|
|
10209
|
+
event.preventDefault();
|
|
10210
|
+
onQueryChange("");
|
|
10211
|
+
},
|
|
10212
|
+
onInteractOutside: (event) => {
|
|
10213
|
+
if (tileRef.current?.contains(event.target)) {
|
|
10214
|
+
event.preventDefault();
|
|
10215
|
+
return;
|
|
10216
|
+
}
|
|
10217
|
+
closedFromOutside.current = true;
|
|
10218
|
+
},
|
|
10219
|
+
onCloseAutoFocus: (event) => {
|
|
10220
|
+
event.preventDefault();
|
|
10221
|
+
if (!closedFromOutside.current) {
|
|
10222
|
+
skipNextTooltip.current = true;
|
|
10223
|
+
tileRef.current?.focus();
|
|
10224
|
+
skipNextTooltip.current = false;
|
|
10225
|
+
}
|
|
10226
|
+
closedFromOutside.current = false;
|
|
10227
|
+
},
|
|
10228
|
+
children: [
|
|
10229
|
+
/* @__PURE__ */ jsx(
|
|
10230
|
+
"div",
|
|
10231
|
+
{
|
|
10232
|
+
"data-slot": "floating-sidebar-flyout-title",
|
|
10233
|
+
className: "text-muted-foreground px-3 pb-2.5 text-[11px] font-medium tracking-[.1em] uppercase",
|
|
10234
|
+
children: module.label
|
|
10235
|
+
}
|
|
10236
|
+
),
|
|
10237
|
+
isEmpty && emptyMessage,
|
|
10238
|
+
!isEmpty && /* @__PURE__ */ jsx(
|
|
10239
|
+
ModuleSearch,
|
|
10240
|
+
{
|
|
10241
|
+
moduleLabel: module.label,
|
|
10242
|
+
query,
|
|
10243
|
+
focusable: flyoutOpen,
|
|
10244
|
+
labels,
|
|
10245
|
+
onQueryChange,
|
|
10246
|
+
className: "mx-2 mb-2"
|
|
10247
|
+
}
|
|
10248
|
+
),
|
|
10249
|
+
/* @__PURE__ */ jsxs(
|
|
10250
|
+
"div",
|
|
10251
|
+
{
|
|
10252
|
+
"data-slot": "floating-sidebar-flyout-list",
|
|
10253
|
+
"data-scroll": "muted",
|
|
10254
|
+
className: cn(
|
|
10255
|
+
// Clip x: items nudge 2px right on hover, and a y-only scroller
|
|
10256
|
+
// would then scroll sideways.
|
|
10257
|
+
"max-h-[420px] min-h-0 overflow-x-hidden overflow-y-auto",
|
|
10258
|
+
SUBTLE_SCROLLBAR
|
|
10259
|
+
),
|
|
10260
|
+
children: [
|
|
10261
|
+
!isEmpty && /* @__PURE__ */ jsx(
|
|
10262
|
+
"ul",
|
|
10263
|
+
{
|
|
10264
|
+
"data-slot": "floating-sidebar-item-list",
|
|
10265
|
+
className: "flex flex-col gap-0.5",
|
|
10266
|
+
children: items.map((item) => /* @__PURE__ */ jsx(
|
|
10267
|
+
ItemRow,
|
|
10268
|
+
{
|
|
10269
|
+
item,
|
|
10270
|
+
list,
|
|
10271
|
+
visible: flyoutOpen,
|
|
10272
|
+
onSelect: (navigates) => onSelect(item, navigates)
|
|
10273
|
+
},
|
|
10274
|
+
item.id
|
|
10275
|
+
))
|
|
10276
|
+
}
|
|
10277
|
+
),
|
|
10278
|
+
!isEmpty && /* @__PURE__ */ jsx(
|
|
10279
|
+
NoMatches,
|
|
10280
|
+
{
|
|
10281
|
+
show: items.length === 0,
|
|
10282
|
+
moduleLabel: module.label,
|
|
10283
|
+
query,
|
|
10284
|
+
labels,
|
|
10285
|
+
compact: true
|
|
10286
|
+
}
|
|
10287
|
+
)
|
|
10288
|
+
]
|
|
10289
|
+
}
|
|
10290
|
+
)
|
|
10291
|
+
]
|
|
10292
|
+
}
|
|
10293
|
+
)
|
|
10294
|
+
]
|
|
10295
|
+
}
|
|
10296
|
+
) });
|
|
10297
|
+
}
|
|
10298
|
+
function ItemRow({ item, list, visible, staggerIndex, onSelect }) {
|
|
10299
|
+
const { activeItemId, favoriteIds, reduced, labels, resolveIcon, renderLink } = list;
|
|
10300
|
+
const active = item.id === activeItemId;
|
|
10301
|
+
const favorite = favoriteIds?.includes(item.id) ?? false;
|
|
10302
|
+
const tabIndex = visible ? 0 : -1;
|
|
10303
|
+
const staggered = staggerIndex !== void 0;
|
|
10304
|
+
const delay = staggered && visible ? Math.min(staggerIndex * 20, FADE_MS) : 0;
|
|
10305
|
+
const rowStyle = staggered ? {
|
|
10306
|
+
opacity: visible ? 1 : 0,
|
|
10307
|
+
transition: transition2(
|
|
10308
|
+
reduced,
|
|
10309
|
+
t("opacity", FADE_MS, delay),
|
|
10310
|
+
"background-color 180ms ease-out"
|
|
10311
|
+
)
|
|
10312
|
+
} : { transition: "background-color 180ms ease-out" };
|
|
10313
|
+
const className = cn(
|
|
10314
|
+
"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",
|
|
10315
|
+
active ? "text-primary font-bold" : "font-normal text-floating-sidebar-item-foreground hover:translate-x-0.5 motion-reduce:hover:translate-x-0"
|
|
10316
|
+
);
|
|
10317
|
+
const content = /* @__PURE__ */ jsxs(Fragment$1, { children: [
|
|
10318
|
+
/* @__PURE__ */ jsx(
|
|
10319
|
+
"span",
|
|
10320
|
+
{
|
|
10321
|
+
className: cn("flex shrink-0", active ? "text-primary" : "text-muted-foreground"),
|
|
10322
|
+
children: /* @__PURE__ */ jsx(SidebarIcon, { icon: item.icon, resolveIcon, size: "sm" })
|
|
10323
|
+
}
|
|
10324
|
+
),
|
|
10325
|
+
/* @__PURE__ */ jsx("span", { className: "min-w-0 [overflow-wrap:anywhere]", children: item.label })
|
|
10326
|
+
] });
|
|
10327
|
+
const common = {
|
|
10328
|
+
className,
|
|
10329
|
+
tabIndex,
|
|
10330
|
+
"aria-current": active ? "page" : void 0,
|
|
10331
|
+
"data-slot": "floating-sidebar-item",
|
|
10332
|
+
"data-active": active ? "true" : void 0
|
|
10333
|
+
};
|
|
10334
|
+
let control;
|
|
10335
|
+
if (item.href && !item.disabled) {
|
|
10336
|
+
const linkProps = {
|
|
10337
|
+
...common,
|
|
10338
|
+
href: item.href,
|
|
10339
|
+
children: content,
|
|
10340
|
+
// Only a plain click on an in-app link moves the current page; external links and
|
|
10341
|
+
// modifier or middle clicks open elsewhere and leave this tab where it is.
|
|
10342
|
+
onClick: (event) => onSelect(
|
|
10343
|
+
!item.external && event.button === 0 && !(event.ctrlKey || event.metaKey || event.shiftKey || event.altKey)
|
|
10344
|
+
),
|
|
10345
|
+
...item.external ? { target: "_blank", rel: "noopener noreferrer" } : {}
|
|
10346
|
+
};
|
|
10347
|
+
control = renderLink ? renderLink(linkProps, item) : /* @__PURE__ */ jsx("a", { ...linkProps });
|
|
10348
|
+
} else {
|
|
10349
|
+
control = /* @__PURE__ */ jsx(
|
|
10350
|
+
"button",
|
|
10351
|
+
{
|
|
10352
|
+
type: "button",
|
|
10353
|
+
...common,
|
|
10354
|
+
disabled: item.disabled,
|
|
10355
|
+
onClick: () => onSelect(false),
|
|
10356
|
+
children: content
|
|
10357
|
+
}
|
|
10358
|
+
);
|
|
10359
|
+
}
|
|
10360
|
+
const favoriteLabel = favorite ? labels.removeFavorite(item.label) : labels.addFavorite(item.label);
|
|
10361
|
+
return /* @__PURE__ */ jsxs(
|
|
10362
|
+
"li",
|
|
10363
|
+
{
|
|
10364
|
+
"data-slot": "floating-sidebar-item-row",
|
|
10365
|
+
className: cn(
|
|
10366
|
+
"group/row relative flex shrink-0 items-start rounded-[10px]",
|
|
10367
|
+
active ? "bg-accent/70" : "hover:bg-floating-sidebar-hover"
|
|
10368
|
+
),
|
|
10369
|
+
style: rowStyle,
|
|
10370
|
+
children: [
|
|
10371
|
+
/* @__PURE__ */ jsx(
|
|
10372
|
+
"span",
|
|
10373
|
+
{
|
|
10374
|
+
"aria-hidden": "true",
|
|
10375
|
+
"data-slot": "floating-sidebar-item-indicator",
|
|
10376
|
+
className: "bg-floating-sidebar-indicator absolute top-3.5 bottom-3.5 left-0 w-[3px] rounded-r-[3px]",
|
|
10377
|
+
style: { opacity: active ? 1 : 0, transition: "opacity 160ms ease-out" }
|
|
10378
|
+
}
|
|
10379
|
+
),
|
|
10380
|
+
control,
|
|
10381
|
+
favoriteIds && item.favoritable !== false && /* @__PURE__ */ jsx(
|
|
10382
|
+
"button",
|
|
10383
|
+
{
|
|
10384
|
+
type: "button",
|
|
10385
|
+
"data-slot": "floating-sidebar-favorite",
|
|
10386
|
+
"data-favorite": favorite ? "true" : void 0,
|
|
10387
|
+
"aria-pressed": favorite,
|
|
10388
|
+
"aria-label": favoriteLabel,
|
|
10389
|
+
title: favoriteLabel,
|
|
10390
|
+
tabIndex,
|
|
10391
|
+
onClick: (event) => {
|
|
10392
|
+
event.stopPropagation();
|
|
10393
|
+
if (!reduced) {
|
|
10394
|
+
event.currentTarget.animate?.(
|
|
10395
|
+
[
|
|
10396
|
+
{ transform: "scale(1)" },
|
|
10397
|
+
{ transform: "scale(1.3)" },
|
|
10398
|
+
{ transform: "scale(1)" }
|
|
10399
|
+
],
|
|
10400
|
+
{ duration: 220, easing: "ease-out" }
|
|
10401
|
+
);
|
|
10402
|
+
}
|
|
10403
|
+
list.onToggleFavorite(item.id);
|
|
10404
|
+
},
|
|
10405
|
+
className: cn(
|
|
10406
|
+
"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",
|
|
10407
|
+
"hover:text-secondary",
|
|
10408
|
+
// Stars stay hidden until their row is hovered or focused, unless favorited.
|
|
10409
|
+
favorite ? "text-secondary opacity-100" : (
|
|
10410
|
+
// Revealed on row hover or focus; always shown on touch, where there is no hover.
|
|
10411
|
+
"text-muted-foreground opacity-0 group-focus-within/row:opacity-100 group-hover/row:opacity-100 [@media(hover:none)]:opacity-100"
|
|
10412
|
+
)
|
|
10413
|
+
),
|
|
10414
|
+
style: {
|
|
10415
|
+
transition: "color 120ms ease-out, background 120ms ease-out, opacity 120ms ease-out"
|
|
10416
|
+
},
|
|
10417
|
+
children: /* @__PURE__ */ jsx(
|
|
10418
|
+
Star,
|
|
10419
|
+
{
|
|
10420
|
+
"aria-hidden": "true",
|
|
10421
|
+
className: cn("size-3.5", favorite && "fill-secondary/10"),
|
|
10422
|
+
strokeWidth: 1.5
|
|
10423
|
+
}
|
|
10424
|
+
)
|
|
10425
|
+
}
|
|
10426
|
+
)
|
|
10427
|
+
]
|
|
10428
|
+
}
|
|
10429
|
+
);
|
|
10430
|
+
}
|
|
10431
|
+
var AppHubContext = createContext(null);
|
|
10432
|
+
function useAppHubContext() {
|
|
10433
|
+
const context = use(AppHubContext);
|
|
10434
|
+
if (!context) throw new Error("AppHub parts must be rendered inside <AppHub>.");
|
|
10435
|
+
return context;
|
|
10436
|
+
}
|
|
10437
|
+
|
|
10438
|
+
// src/components/compounds/AppHub/app-hub.constants.ts
|
|
10439
|
+
var DEFAULT_APP_HUB_ID = "app-hub";
|
|
10440
|
+
var DEFAULT_STORAGE_KEY = "tcce-app-hub:open";
|
|
10441
|
+
var OPEN_CHANGE_EVENT = "tcce-app-hub:open-change";
|
|
10442
|
+
var ITEM_SELECTOR = "[data-app-hub-item]";
|
|
10443
|
+
var ENTRY_SELECTOR = "[data-app-hub-entry]";
|
|
10444
|
+
var FOCUS_RING = "outline-hidden focus-visible:outline-2 focus-visible:outline-solid focus-visible:outline-offset-3 focus-visible:outline-main-blue-primary";
|
|
10445
|
+
var TOOLTIP_CLASS = "rounded-lg px-2.75 py-1.75 text-[11px] leading-none font-semibold shadow-black-button motion-reduce:animate-none";
|
|
10446
|
+
var DEFAULT_APP_HUB_LABELS = {
|
|
10447
|
+
navigation: "Applications",
|
|
10448
|
+
allApps: "All applications",
|
|
10449
|
+
sheetTitle: "Applications",
|
|
10450
|
+
hideDock: "Hide dock",
|
|
10451
|
+
showDock: "show applications dock",
|
|
10452
|
+
tab: "Apps",
|
|
10453
|
+
fab: "Apps",
|
|
10454
|
+
pending: (count) => `${count} pending`,
|
|
10455
|
+
leaves: (appName) => `leaves ${appName}`,
|
|
10456
|
+
current: "Current",
|
|
10457
|
+
noAccess: "No access \xB7 ask your manager",
|
|
10458
|
+
searchLabel: "Search applications",
|
|
10459
|
+
searchPlaceholder: "Search apps",
|
|
10460
|
+
clearSearch: "Clear search",
|
|
10461
|
+
resultCount: (count) => count === 1 ? "1 application found" : `${count} applications found`,
|
|
10462
|
+
loading: "Loading applications",
|
|
10463
|
+
empty: "No applications found",
|
|
10464
|
+
noMatch: (query) => `No apps match \u201C${query}\u201D`,
|
|
10465
|
+
noMatchDescription: "Check the spelling or clear the search.",
|
|
10466
|
+
error: "Couldn\u2019t load applications",
|
|
10467
|
+
errorDescription: "The apps in the dock still work.",
|
|
10468
|
+
retry: "Try again",
|
|
10469
|
+
directory: "View all applications",
|
|
10470
|
+
close: "Close",
|
|
10471
|
+
dockHidden: "Applications dock hidden. Use the Apps tab on the right edge to show it again.",
|
|
10472
|
+
dockShown: "Applications dock shown"
|
|
10473
|
+
};
|
|
10474
|
+
|
|
10475
|
+
// src/components/compounds/AppHub/app-hub.utils.ts
|
|
10476
|
+
function mergeLabels(defaults, overrides = {}) {
|
|
10477
|
+
const defined = Object.entries(overrides).filter(([, value]) => value !== void 0);
|
|
10478
|
+
return { ...defaults, ...Object.fromEntries(defined) };
|
|
10479
|
+
}
|
|
10480
|
+
function areLabelsEqual(a, b) {
|
|
10481
|
+
const keys = Object.keys(a);
|
|
10482
|
+
return keys.every((key) => Object.is(a[key], b[key]));
|
|
10483
|
+
}
|
|
10484
|
+
function normalizePending(pending) {
|
|
10485
|
+
return typeof pending === "number" && Number.isFinite(pending) && pending >= 1 ? Math.floor(pending) : 0;
|
|
10486
|
+
}
|
|
10487
|
+
function formatPendingCount(count) {
|
|
10488
|
+
return count > 9 ? "9+" : String(count);
|
|
10489
|
+
}
|
|
10490
|
+
function sumPending(apps) {
|
|
10491
|
+
return apps.reduce((total, app) => total + (app.locked ? 0 : normalizePending(app.pending)), 0);
|
|
10492
|
+
}
|
|
10493
|
+
function getAppAccessibleName(app, labels, appName) {
|
|
10494
|
+
const pending = normalizePending(app.pending);
|
|
10495
|
+
return [
|
|
10496
|
+
app.label,
|
|
10497
|
+
pending > 0 && labels.pending(pending),
|
|
10498
|
+
app.external && labels.leaves(appName)
|
|
10499
|
+
].filter(Boolean).join(", ");
|
|
10500
|
+
}
|
|
10501
|
+
function getEntryElement(container) {
|
|
10502
|
+
return container?.querySelector(ENTRY_SELECTOR) ?? container?.querySelector(ITEM_SELECTOR) ?? null;
|
|
10503
|
+
}
|
|
10504
|
+
function groupApps(apps) {
|
|
10505
|
+
const sections = /* @__PURE__ */ new Map([[void 0, []]]);
|
|
10506
|
+
for (const app of apps) {
|
|
10507
|
+
const key = app.group || void 0;
|
|
10508
|
+
sections.set(key, [...sections.get(key) ?? [], app]);
|
|
10509
|
+
}
|
|
10510
|
+
return [...sections].filter(([, items]) => items.length > 0).map(([heading, items]) => ({ heading, apps: items }));
|
|
10511
|
+
}
|
|
10512
|
+
function filterApps(apps, query) {
|
|
10513
|
+
const needle = query.trim().toLowerCase();
|
|
10514
|
+
if (!needle) return apps;
|
|
10515
|
+
return apps.filter(
|
|
10516
|
+
(app) => `${app.label} ${app.description ?? ""}`.toLowerCase().includes(needle)
|
|
10517
|
+
);
|
|
10518
|
+
}
|
|
10519
|
+
var appHubIconVariants = cva("relative flex shrink-0 items-center justify-center", {
|
|
10520
|
+
variants: {
|
|
10521
|
+
tone: {
|
|
10522
|
+
green: "bg-green-100 text-green-800",
|
|
10523
|
+
yellow: "bg-yellow-200 text-yellow-800",
|
|
10524
|
+
navy: "bg-main-blue-primary/10 text-main-blue-primary",
|
|
10525
|
+
orange: "bg-orange-100 text-orange-800",
|
|
10526
|
+
purple: "bg-purple-100 text-purple-600",
|
|
10527
|
+
grey: "bg-light-200 text-main-grey"
|
|
10528
|
+
},
|
|
10529
|
+
size: {
|
|
10530
|
+
tile: "size-12 rounded-2xl [&_svg]:size-5",
|
|
10531
|
+
row: "size-9 rounded-xl [&_svg]:size-4.5",
|
|
10532
|
+
sheet: "size-10 rounded-xl [&_svg]:size-5"
|
|
10533
|
+
},
|
|
10534
|
+
// Declared last so `cn()` keeps the current fill over the tone.
|
|
10535
|
+
current: { true: "bg-main-blue-primary text-main-white", false: "" }
|
|
10536
|
+
},
|
|
10537
|
+
defaultVariants: { tone: "grey", size: "tile", current: false }
|
|
10538
|
+
});
|
|
10539
|
+
function AppHubLink({
|
|
10540
|
+
app,
|
|
10541
|
+
afterNavigate,
|
|
10542
|
+
onClick,
|
|
10543
|
+
...props
|
|
10544
|
+
}) {
|
|
10545
|
+
const { renderLink, onNavigate } = useAppHubContext();
|
|
10546
|
+
const linkProps = {
|
|
10547
|
+
...props,
|
|
10548
|
+
href: app.href,
|
|
10549
|
+
onClick: (event) => {
|
|
10550
|
+
onClick?.(event);
|
|
10551
|
+
const modified = event.defaultPrevented || event.button !== 0 || event.metaKey || event.ctrlKey || event.shiftKey || event.altKey;
|
|
10552
|
+
if (modified) return;
|
|
10553
|
+
onNavigate?.(app, event);
|
|
10554
|
+
afterNavigate?.();
|
|
10555
|
+
}
|
|
10556
|
+
};
|
|
10557
|
+
return renderLink ? renderLink(linkProps, app) : /* @__PURE__ */ jsx("a", { ...linkProps });
|
|
10558
|
+
}
|
|
10559
|
+
function AppHubCounter({ count, className }) {
|
|
10560
|
+
if (count <= 0) return null;
|
|
10561
|
+
return /* @__PURE__ */ jsx(
|
|
10562
|
+
"span",
|
|
10563
|
+
{
|
|
10564
|
+
"aria-hidden": "true",
|
|
10565
|
+
"data-slot": "app-hub-counter",
|
|
10566
|
+
className: cn(
|
|
10567
|
+
"bg-main-orange-primary text-background ring-background absolute -top-1 -right-1 flex h-4.5 min-w-4.5 items-center justify-center rounded-full px-1 text-[11px] font-bold ring-2",
|
|
10568
|
+
className
|
|
10569
|
+
),
|
|
10570
|
+
children: formatPendingCount(count)
|
|
10571
|
+
}
|
|
10572
|
+
);
|
|
10573
|
+
}
|
|
10574
|
+
function AppHubTile({ app, current = false }) {
|
|
10575
|
+
const { labels, appName } = useAppHubContext();
|
|
10576
|
+
const name = getAppAccessibleName(app, labels, appName);
|
|
10577
|
+
const tone = app.tone ?? "grey";
|
|
10578
|
+
return /* @__PURE__ */ jsxs(Tooltip, { children: [
|
|
10579
|
+
/* @__PURE__ */ jsx(TooltipTrigger, { asChild: true, "aria-describedby": void 0, children: /* @__PURE__ */ jsxs(
|
|
10580
|
+
AppHubLink,
|
|
10581
|
+
{
|
|
10582
|
+
app,
|
|
10583
|
+
"aria-label": name,
|
|
10584
|
+
"aria-current": current ? "page" : void 0,
|
|
10585
|
+
"data-slot": "app-hub-tile",
|
|
10586
|
+
"data-variant": tone,
|
|
10587
|
+
"data-current": current ? "" : void 0,
|
|
10588
|
+
"data-app-hub-item": "",
|
|
10589
|
+
"data-app-hub-entry": current ? "" : void 0,
|
|
10590
|
+
className: cn(
|
|
10591
|
+
appHubIconVariants({ tone, size: "tile", current }),
|
|
10592
|
+
"hover:shadow-black-button transition-[translate,box-shadow] hover:-translate-y-0.5 active:translate-y-px active:shadow-none motion-reduce:transition-none",
|
|
10593
|
+
"data-current:before:bg-main-orange-primary data-current:before:absolute data-current:before:-left-2.5 data-current:before:h-5 data-current:before:w-0.75 data-current:before:rounded-full",
|
|
10594
|
+
FOCUS_RING
|
|
10595
|
+
),
|
|
10596
|
+
children: [
|
|
10597
|
+
/* @__PURE__ */ jsx("span", { "aria-hidden": "true", className: "contents", children: app.icon }),
|
|
10598
|
+
/* @__PURE__ */ jsx(AppHubCounter, { count: normalizePending(app.pending) })
|
|
10599
|
+
]
|
|
10600
|
+
}
|
|
10601
|
+
) }),
|
|
10602
|
+
/* @__PURE__ */ jsx(
|
|
10603
|
+
TooltipContent,
|
|
10604
|
+
{
|
|
10605
|
+
"aria-hidden": "true",
|
|
10606
|
+
side: "left",
|
|
10607
|
+
variant: "primary",
|
|
10608
|
+
size: "sm",
|
|
10609
|
+
className: TOOLTIP_CLASS,
|
|
10610
|
+
children: app.label
|
|
10611
|
+
}
|
|
10612
|
+
)
|
|
10613
|
+
] });
|
|
10614
|
+
}
|
|
10615
|
+
var NAV_KEYS = /* @__PURE__ */ new Set(["ArrowDown", "ArrowUp", "Home", "End"]);
|
|
10616
|
+
function useArrowNavigation(containerRef) {
|
|
10617
|
+
return useCallback(
|
|
10618
|
+
(event) => {
|
|
10619
|
+
const container = containerRef.current;
|
|
10620
|
+
const target = event.target;
|
|
10621
|
+
if (event.defaultPrevented || !container || !NAV_KEYS.has(event.key)) return;
|
|
10622
|
+
if (!(target instanceof Element) || !container.contains(target)) return;
|
|
10623
|
+
const current = target.closest(ITEM_SELECTOR);
|
|
10624
|
+
if (!current) return;
|
|
10625
|
+
const items = Array.from(container.querySelectorAll(ITEM_SELECTOR)).filter(
|
|
10626
|
+
(item) => !item.closest("[inert]")
|
|
10627
|
+
);
|
|
10628
|
+
const index = items.indexOf(current);
|
|
10629
|
+
if (index === -1) return;
|
|
10630
|
+
event.preventDefault();
|
|
10631
|
+
const last = items.length - 1;
|
|
10632
|
+
const next = event.key === "Home" ? 0 : event.key === "End" ? last : event.key === "ArrowDown" ? (index + 1) % items.length : (index - 1 + items.length) % items.length;
|
|
10633
|
+
items[next]?.focus();
|
|
10634
|
+
},
|
|
10635
|
+
[containerRef]
|
|
10636
|
+
);
|
|
10637
|
+
}
|
|
10638
|
+
var SKELETON_ROWS = [
|
|
10639
|
+
["w-[58%]", "w-[82%]"],
|
|
10640
|
+
["w-[44%]", "w-[70%]"],
|
|
10641
|
+
["w-[64%]", "w-[76%]"]
|
|
10642
|
+
];
|
|
10643
|
+
function AppHubRow({
|
|
10644
|
+
app,
|
|
10645
|
+
current,
|
|
10646
|
+
variant,
|
|
10647
|
+
onActivate
|
|
10648
|
+
}) {
|
|
10649
|
+
const { labels, appName } = useAppHubContext();
|
|
10650
|
+
const descId = useId();
|
|
10651
|
+
const locked = app.locked === true;
|
|
10652
|
+
const pending = normalizePending(app.pending);
|
|
10653
|
+
const className = cn(
|
|
10654
|
+
"hover:bg-light-200 flex w-full items-center gap-3 rounded-xl p-2 text-left",
|
|
10655
|
+
variant === "panel" ? "min-h-13" : "min-h-14",
|
|
10656
|
+
current && "bg-light-200",
|
|
10657
|
+
FOCUS_RING
|
|
10658
|
+
);
|
|
10659
|
+
const body = /* @__PURE__ */ jsxs(Fragment$1, { children: [
|
|
10660
|
+
/* @__PURE__ */ jsx(
|
|
10661
|
+
"span",
|
|
10662
|
+
{
|
|
10663
|
+
"aria-hidden": "true",
|
|
10664
|
+
className: appHubIconVariants({
|
|
10665
|
+
tone: locked ? "grey" : app.tone ?? "grey",
|
|
10666
|
+
size: variant === "panel" ? "row" : "sheet"
|
|
10667
|
+
}),
|
|
10668
|
+
children: app.icon
|
|
10669
|
+
}
|
|
10670
|
+
),
|
|
10671
|
+
/* @__PURE__ */ jsxs("span", { className: "flex min-w-0 flex-1 flex-col", children: [
|
|
10672
|
+
/* @__PURE__ */ jsxs(
|
|
10673
|
+
"span",
|
|
10674
|
+
{
|
|
10675
|
+
className: cn(
|
|
10676
|
+
"text-light-1000 flex items-center gap-1.5 font-medium",
|
|
10677
|
+
variant === "panel" ? "text-sm" : "text-[15px]"
|
|
10678
|
+
),
|
|
10679
|
+
children: [
|
|
10680
|
+
/* @__PURE__ */ jsx("span", { className: "truncate", children: app.label }),
|
|
10681
|
+
app.external && /* @__PURE__ */ jsx(
|
|
10682
|
+
ArrowUpRight,
|
|
10683
|
+
{
|
|
10684
|
+
"aria-hidden": "true",
|
|
10685
|
+
className: "text-main-grey size-3.25 shrink-0"
|
|
10686
|
+
}
|
|
10687
|
+
)
|
|
10688
|
+
]
|
|
10689
|
+
}
|
|
10690
|
+
),
|
|
10691
|
+
locked ? /* @__PURE__ */ jsxs("span", { className: "text-main-grey flex items-center gap-1 text-xs", children: [
|
|
10692
|
+
/* @__PURE__ */ jsx(Lock, { "aria-hidden": "true", className: "size-3" }),
|
|
10693
|
+
labels.noAccess
|
|
10694
|
+
] }) : app.description && /* @__PURE__ */ jsx(
|
|
10695
|
+
"span",
|
|
10696
|
+
{
|
|
10697
|
+
id: descId,
|
|
10698
|
+
className: cn(
|
|
10699
|
+
"text-main-grey truncate",
|
|
10700
|
+
variant === "panel" ? "text-xs" : "text-[13px]"
|
|
10701
|
+
),
|
|
10702
|
+
children: app.description
|
|
10703
|
+
}
|
|
10704
|
+
)
|
|
10705
|
+
] }),
|
|
10706
|
+
current && /* @__PURE__ */ jsx(
|
|
10707
|
+
"span",
|
|
10708
|
+
{
|
|
10709
|
+
"aria-hidden": "true",
|
|
10710
|
+
className: "bg-main-blue-primary text-main-white shrink-0 rounded-full px-2 py-0.5 text-[11px] leading-tight font-semibold",
|
|
10711
|
+
children: labels.current
|
|
10712
|
+
}
|
|
10713
|
+
),
|
|
10714
|
+
pending > 0 && !locked && /* @__PURE__ */ jsx(
|
|
10715
|
+
"span",
|
|
10716
|
+
{
|
|
10717
|
+
"aria-hidden": "true",
|
|
10718
|
+
"data-slot": "app-hub-row-counter",
|
|
10719
|
+
className: cn(
|
|
10720
|
+
"bg-main-orange-primary text-background flex shrink-0 items-center justify-center rounded-full font-bold",
|
|
10721
|
+
variant === "panel" ? "h-5 min-w-5 px-1.5 text-[11px]" : "h-5.5 min-w-5.5 px-1.75 text-xs"
|
|
10722
|
+
),
|
|
10723
|
+
children: formatPendingCount(pending)
|
|
10724
|
+
}
|
|
10725
|
+
)
|
|
10726
|
+
] });
|
|
10727
|
+
if (locked) {
|
|
10728
|
+
return /* @__PURE__ */ jsx(
|
|
10729
|
+
"button",
|
|
10730
|
+
{
|
|
10731
|
+
type: "button",
|
|
10732
|
+
"aria-disabled": "true",
|
|
10733
|
+
"data-slot": "app-hub-row",
|
|
10734
|
+
"data-app-hub-item": "",
|
|
10735
|
+
className: cn(className, "text-main-grey cursor-not-allowed"),
|
|
10736
|
+
children: body
|
|
10737
|
+
}
|
|
10738
|
+
);
|
|
10739
|
+
}
|
|
10740
|
+
return /* @__PURE__ */ jsx(
|
|
10741
|
+
AppHubLink,
|
|
10742
|
+
{
|
|
10743
|
+
app,
|
|
10744
|
+
afterNavigate: onActivate,
|
|
10745
|
+
"aria-label": getAppAccessibleName(app, labels, appName),
|
|
10746
|
+
"aria-current": current ? "page" : void 0,
|
|
10747
|
+
"aria-describedby": app.description ? descId : void 0,
|
|
10748
|
+
"data-slot": "app-hub-row",
|
|
10749
|
+
"data-app-hub-item": "",
|
|
10750
|
+
className,
|
|
10751
|
+
children: body
|
|
10752
|
+
}
|
|
10753
|
+
);
|
|
10754
|
+
}
|
|
10755
|
+
function AppHubCatalog({
|
|
10756
|
+
variant,
|
|
10757
|
+
apps,
|
|
10758
|
+
currentAppId,
|
|
10759
|
+
status,
|
|
10760
|
+
onRetry,
|
|
10761
|
+
directoryHref,
|
|
10762
|
+
searchThreshold,
|
|
10763
|
+
onActivate
|
|
10764
|
+
}) {
|
|
10765
|
+
const { labels, renderLink } = useAppHubContext();
|
|
10766
|
+
const rootRef = useRef(null);
|
|
10767
|
+
const onKeyDown = useArrowNavigation(rootRef);
|
|
10768
|
+
const searchId = useId();
|
|
10769
|
+
const searchRef = useRef(null);
|
|
10770
|
+
const [query, setQuery] = useState("");
|
|
10771
|
+
const directoryProps = {
|
|
10772
|
+
href: directoryHref ?? "",
|
|
10773
|
+
"data-slot": "app-hub-directory",
|
|
10774
|
+
"data-app-hub-item": "",
|
|
10775
|
+
onClick: () => onActivate?.(),
|
|
10776
|
+
className: cn(
|
|
10777
|
+
"text-main-blue-primary flex items-center gap-2 font-semibold",
|
|
10778
|
+
variant === "panel" ? "border-light-200 hover:bg-light-200 justify-between border-t px-4 py-3 text-[13px]" : "border-light-300 mx-4 mb-4 min-h-13 justify-center rounded-full border text-sm",
|
|
10779
|
+
FOCUS_RING
|
|
10780
|
+
),
|
|
10781
|
+
children: /* @__PURE__ */ jsxs(Fragment$1, { children: [
|
|
10782
|
+
labels.directory,
|
|
10783
|
+
/* @__PURE__ */ jsx(ArrowRight, { "aria-hidden": "true", className: "size-4 shrink-0" })
|
|
10784
|
+
] })
|
|
10785
|
+
};
|
|
10786
|
+
const ready = status === "ready";
|
|
10787
|
+
const searchVisible = variant === "panel" && ready && apps.length >= searchThreshold;
|
|
10788
|
+
if (!searchVisible && query) setQuery("");
|
|
10789
|
+
const activeQuery = searchVisible ? query : "";
|
|
10790
|
+
const filtered = filterApps(apps, activeQuery);
|
|
10791
|
+
const message = status === "loading" ? labels.loading : activeQuery && labels.resultCount(filtered.length);
|
|
10792
|
+
const [live, setLive] = useState("");
|
|
10793
|
+
useEffect(() => setLive(message), [message]);
|
|
10794
|
+
return /* @__PURE__ */ jsxs(
|
|
10795
|
+
"div",
|
|
10796
|
+
{
|
|
10797
|
+
ref: rootRef,
|
|
10798
|
+
"data-slot": "app-hub-catalog",
|
|
10799
|
+
"data-variant": variant,
|
|
10800
|
+
tabIndex: -1,
|
|
10801
|
+
onKeyDown,
|
|
10802
|
+
className: "flex min-h-0 flex-1 flex-col outline-none",
|
|
10803
|
+
children: [
|
|
10804
|
+
searchVisible && /* @__PURE__ */ jsxs("div", { "data-slot": "app-hub-search", className: "relative shrink-0 px-3 pt-3", children: [
|
|
10805
|
+
/* @__PURE__ */ jsx("label", { htmlFor: searchId, className: "sr-only", children: labels.searchLabel }),
|
|
10806
|
+
/* @__PURE__ */ jsx(
|
|
10807
|
+
Search,
|
|
10808
|
+
{
|
|
10809
|
+
"aria-hidden": "true",
|
|
10810
|
+
className: "text-main-grey pointer-events-none absolute top-[calc(50%+6px)] left-6 size-4 -translate-y-1/2"
|
|
10811
|
+
}
|
|
10812
|
+
),
|
|
10813
|
+
/* @__PURE__ */ jsx(
|
|
10814
|
+
Input,
|
|
10815
|
+
{
|
|
10816
|
+
ref: searchRef,
|
|
10817
|
+
id: searchId,
|
|
10818
|
+
type: "search",
|
|
10819
|
+
value: query,
|
|
10820
|
+
placeholder: labels.searchPlaceholder,
|
|
10821
|
+
onChange: (event) => setQuery(event.target.value),
|
|
10822
|
+
className: "border-main-grey/70 bg-background h-10 rounded-lg px-9 text-sm [&::-webkit-search-cancel-button]:appearance-none"
|
|
10823
|
+
}
|
|
10824
|
+
),
|
|
10825
|
+
query && /* @__PURE__ */ jsx(
|
|
10826
|
+
"button",
|
|
10827
|
+
{
|
|
10828
|
+
type: "button",
|
|
10829
|
+
"aria-label": labels.clearSearch,
|
|
10830
|
+
onClick: () => {
|
|
10831
|
+
setQuery("");
|
|
10832
|
+
searchRef.current?.focus();
|
|
10833
|
+
},
|
|
10834
|
+
className: cn(
|
|
10835
|
+
"text-main-grey hover:bg-light-200 absolute top-[calc(50%+6px)] right-4 flex size-8 -translate-y-1/2 items-center justify-center rounded-lg [&_svg]:size-3.5",
|
|
10836
|
+
FOCUS_RING
|
|
10837
|
+
),
|
|
10838
|
+
children: /* @__PURE__ */ jsx(X, { "aria-hidden": "true" })
|
|
10839
|
+
}
|
|
10840
|
+
)
|
|
10841
|
+
] }),
|
|
10842
|
+
/* @__PURE__ */ jsxs(
|
|
10843
|
+
"div",
|
|
10844
|
+
{
|
|
10845
|
+
"data-slot": "app-hub-catalog-scroll",
|
|
10846
|
+
className: cn(
|
|
10847
|
+
"flex min-h-0 flex-1 flex-col gap-2 overflow-y-auto p-2",
|
|
10848
|
+
variant === "panel" && "max-h-74"
|
|
10849
|
+
),
|
|
10850
|
+
children: [
|
|
10851
|
+
/* @__PURE__ */ jsx("div", { role: "status", className: "sr-only", children: live }),
|
|
10852
|
+
status === "loading" && /* @__PURE__ */ jsx(
|
|
10853
|
+
"div",
|
|
10854
|
+
{
|
|
10855
|
+
"data-slot": "app-hub-loading",
|
|
10856
|
+
"aria-hidden": "true",
|
|
10857
|
+
className: "flex flex-col gap-1",
|
|
10858
|
+
children: SKELETON_ROWS.map(([first, second]) => /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3 p-2", children: [
|
|
10859
|
+
/* @__PURE__ */ jsx("span", { className: "bg-light-200 size-9 shrink-0 rounded-xl" }),
|
|
10860
|
+
/* @__PURE__ */ jsxs("span", { className: "flex flex-1 flex-col gap-1.5", children: [
|
|
10861
|
+
/* @__PURE__ */ jsx(
|
|
10862
|
+
"span",
|
|
10863
|
+
{
|
|
10864
|
+
className: cn("bg-light-200 h-2.5 rounded-[5px]", first)
|
|
10865
|
+
}
|
|
10866
|
+
),
|
|
10867
|
+
/* @__PURE__ */ jsx("span", { className: cn("bg-light-200 h-2 rounded-sm", second) })
|
|
10868
|
+
] })
|
|
10869
|
+
] }, first))
|
|
10870
|
+
}
|
|
10871
|
+
),
|
|
10872
|
+
status === "error" && /* @__PURE__ */ jsxs(
|
|
10873
|
+
"div",
|
|
10874
|
+
{
|
|
10875
|
+
role: "alert",
|
|
10876
|
+
"data-slot": "app-hub-error",
|
|
10877
|
+
className: "flex flex-col items-start gap-2.5 px-2 pt-3 pb-2",
|
|
10878
|
+
children: [
|
|
10879
|
+
/* @__PURE__ */ jsx(
|
|
10880
|
+
"span",
|
|
10881
|
+
{
|
|
10882
|
+
"aria-hidden": "true",
|
|
10883
|
+
className: "flex size-9 items-center justify-center rounded-xl bg-red-100 text-red-800",
|
|
10884
|
+
children: /* @__PURE__ */ jsx(CircleAlert, { className: "size-4.5" })
|
|
10885
|
+
}
|
|
10886
|
+
),
|
|
10887
|
+
/* @__PURE__ */ jsxs("span", { className: "flex flex-col gap-1", children: [
|
|
10888
|
+
/* @__PURE__ */ jsx("span", { className: "text-light-1000 text-sm font-semibold", children: labels.error }),
|
|
10889
|
+
/* @__PURE__ */ jsx("span", { className: "text-main-grey text-[13px] leading-snug", children: labels.errorDescription })
|
|
10890
|
+
] }),
|
|
10891
|
+
onRetry && /* @__PURE__ */ jsx(
|
|
10892
|
+
Button,
|
|
10893
|
+
{
|
|
10894
|
+
size: "sm",
|
|
10895
|
+
variant: "outline",
|
|
10896
|
+
"data-app-hub-item": "",
|
|
10897
|
+
onClick: () => {
|
|
10898
|
+
rootRef.current?.focus();
|
|
10899
|
+
onRetry();
|
|
10900
|
+
},
|
|
10901
|
+
children: labels.retry
|
|
10902
|
+
}
|
|
10903
|
+
)
|
|
10904
|
+
]
|
|
10905
|
+
}
|
|
10906
|
+
),
|
|
10907
|
+
ready && filtered.length === 0 && /* @__PURE__ */ jsxs(
|
|
10908
|
+
"div",
|
|
10909
|
+
{
|
|
10910
|
+
"data-slot": "app-hub-empty",
|
|
10911
|
+
className: "flex flex-col items-start gap-1.5 px-2 pt-4 pb-2",
|
|
10912
|
+
children: [
|
|
10913
|
+
/* @__PURE__ */ jsx("span", { className: "text-light-1000 text-sm font-semibold", children: activeQuery ? labels.noMatch(activeQuery) : labels.empty }),
|
|
10914
|
+
activeQuery && /* @__PURE__ */ jsxs(Fragment$1, { children: [
|
|
10915
|
+
/* @__PURE__ */ jsx("span", { className: "text-main-grey text-[13px] leading-snug", children: labels.noMatchDescription }),
|
|
10916
|
+
/* @__PURE__ */ jsx(
|
|
10917
|
+
"button",
|
|
10918
|
+
{
|
|
10919
|
+
type: "button",
|
|
10920
|
+
"data-app-hub-item": "",
|
|
10921
|
+
onClick: () => {
|
|
10922
|
+
setQuery("");
|
|
10923
|
+
searchRef.current?.focus();
|
|
10924
|
+
},
|
|
10925
|
+
className: cn(
|
|
10926
|
+
"text-main-blue-primary mt-1 rounded-sm py-1.5 text-[13px] font-semibold underline underline-offset-3",
|
|
10927
|
+
FOCUS_RING
|
|
10928
|
+
),
|
|
10929
|
+
children: labels.clearSearch
|
|
10930
|
+
}
|
|
10931
|
+
)
|
|
10932
|
+
] })
|
|
10933
|
+
]
|
|
10934
|
+
}
|
|
10935
|
+
),
|
|
10936
|
+
ready && groupApps(filtered).map((section) => /* @__PURE__ */ jsxs("section", { className: "flex flex-col gap-1", children: [
|
|
10937
|
+
section.heading && /* @__PURE__ */ jsx("h3", { className: "text-main-grey px-2 text-[11px] font-semibold uppercase", children: section.heading }),
|
|
10938
|
+
/* @__PURE__ */ jsx("ul", { role: "list", className: "flex flex-col gap-1", children: section.apps.map((app) => /* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsx(
|
|
10939
|
+
AppHubRow,
|
|
10940
|
+
{
|
|
10941
|
+
app,
|
|
10942
|
+
current: app.id === currentAppId,
|
|
10943
|
+
variant,
|
|
10944
|
+
onActivate
|
|
10945
|
+
}
|
|
10946
|
+
) }, app.id)) })
|
|
10947
|
+
] }, section.heading ?? ""))
|
|
10948
|
+
]
|
|
10949
|
+
}
|
|
10950
|
+
),
|
|
10951
|
+
directoryHref && /* @__PURE__ */ jsx(Fragment$1, { children: renderLink ? renderLink(directoryProps, void 0) : /* @__PURE__ */ jsx("a", { ...directoryProps }) })
|
|
10952
|
+
]
|
|
10953
|
+
}
|
|
10954
|
+
);
|
|
10955
|
+
}
|
|
10956
|
+
function AppHubPanel({
|
|
10957
|
+
id,
|
|
10958
|
+
open,
|
|
10959
|
+
onOpenChange,
|
|
10960
|
+
onCloseAutoFocus,
|
|
10961
|
+
...catalog
|
|
10962
|
+
}) {
|
|
10963
|
+
const { labels } = useAppHubContext();
|
|
10964
|
+
const titleId = useId();
|
|
10965
|
+
const contentRef = useRef(null);
|
|
10966
|
+
const [tooltipOpen, setTooltipOpen] = useState(false);
|
|
10967
|
+
if (open && tooltipOpen) setTooltipOpen(false);
|
|
10968
|
+
const quietRef = useRef(false);
|
|
10969
|
+
useEffect(() => {
|
|
10970
|
+
if (open) return () => void (quietRef.current = true);
|
|
10971
|
+
}, [open]);
|
|
10972
|
+
return /* @__PURE__ */ jsxs(Popover, { open, onOpenChange, children: [
|
|
10973
|
+
/* @__PURE__ */ jsxs(
|
|
10974
|
+
Tooltip,
|
|
10975
|
+
{
|
|
10976
|
+
open: tooltipOpen,
|
|
10977
|
+
onOpenChange: (next) => setTooltipOpen(next && !open && !quietRef.current),
|
|
10978
|
+
children: [
|
|
10979
|
+
/* @__PURE__ */ jsx(TooltipTrigger, { asChild: true, "aria-describedby": void 0, children: /* @__PURE__ */ jsx(
|
|
10980
|
+
PopoverTrigger,
|
|
10981
|
+
{
|
|
10982
|
+
"aria-label": labels.allApps,
|
|
10983
|
+
"aria-controls": id,
|
|
10984
|
+
"data-slot": "app-hub-panel-trigger",
|
|
10985
|
+
onPointerEnter: () => void (quietRef.current = false),
|
|
10986
|
+
onBlur: () => void (quietRef.current = false),
|
|
10987
|
+
"data-app-hub-item": "",
|
|
10988
|
+
className: cn(
|
|
10989
|
+
appHubIconVariants({ tone: "grey", size: "tile" }),
|
|
10990
|
+
// Both triggers write `data-state` through `asChild` and the tooltip's value
|
|
10991
|
+
// wins, so the open tint keys off `aria-expanded` instead.
|
|
10992
|
+
"aria-expanded:bg-main-blue-primary/10 aria-expanded:text-main-blue-primary",
|
|
10993
|
+
FOCUS_RING
|
|
10994
|
+
),
|
|
10995
|
+
children: /* @__PURE__ */ jsx(LayoutGrid, { "aria-hidden": "true" })
|
|
10996
|
+
}
|
|
10997
|
+
) }),
|
|
10998
|
+
/* @__PURE__ */ jsx(
|
|
10999
|
+
TooltipContent,
|
|
11000
|
+
{
|
|
11001
|
+
"aria-hidden": "true",
|
|
11002
|
+
side: "left",
|
|
11003
|
+
variant: "primary",
|
|
11004
|
+
size: "sm",
|
|
11005
|
+
className: TOOLTIP_CLASS,
|
|
11006
|
+
children: labels.allApps
|
|
11007
|
+
}
|
|
11008
|
+
)
|
|
11009
|
+
]
|
|
11010
|
+
}
|
|
11011
|
+
),
|
|
11012
|
+
/* @__PURE__ */ jsxs(
|
|
11013
|
+
PopoverContent,
|
|
11014
|
+
{
|
|
11015
|
+
ref: contentRef,
|
|
11016
|
+
id,
|
|
11017
|
+
side: "left",
|
|
11018
|
+
align: "center",
|
|
11019
|
+
sideOffset: 22,
|
|
11020
|
+
collisionPadding: 8,
|
|
11021
|
+
"aria-labelledby": titleId,
|
|
11022
|
+
"data-slot": "app-hub-panel",
|
|
11023
|
+
onCloseAutoFocus,
|
|
11024
|
+
onOpenAutoFocus: (event) => {
|
|
11025
|
+
event.preventDefault();
|
|
11026
|
+
const content = contentRef.current;
|
|
11027
|
+
(content?.querySelector('input[type="search"]') ?? content?.querySelector(ITEM_SELECTOR) ?? content)?.focus();
|
|
11028
|
+
},
|
|
11029
|
+
className: "border-light-300 shadow-black-card max-h-(--radix-popover-content-available-height) w-75 gap-0 rounded-2xl border p-0 motion-reduce:data-[state=closed]:animate-none motion-reduce:data-[state=open]:animate-none",
|
|
11030
|
+
children: [
|
|
11031
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between px-4 pt-3 pb-1", children: [
|
|
11032
|
+
/* @__PURE__ */ jsx(
|
|
11033
|
+
"h2",
|
|
11034
|
+
{
|
|
11035
|
+
id: titleId,
|
|
11036
|
+
className: "font-heading text-main-blue-primary text-base leading-tight font-bold",
|
|
11037
|
+
children: labels.allApps
|
|
11038
|
+
}
|
|
11039
|
+
),
|
|
11040
|
+
/* @__PURE__ */ jsx(
|
|
11041
|
+
"button",
|
|
11042
|
+
{
|
|
11043
|
+
type: "button",
|
|
11044
|
+
"aria-label": labels.close,
|
|
11045
|
+
onClick: () => onOpenChange(false),
|
|
11046
|
+
className: cn(
|
|
11047
|
+
"text-main-grey hover:bg-light-200 flex size-8 items-center justify-center rounded-xl [&_svg]:size-4",
|
|
11048
|
+
FOCUS_RING
|
|
11049
|
+
),
|
|
11050
|
+
children: /* @__PURE__ */ jsx(X, { "aria-hidden": "true" })
|
|
11051
|
+
}
|
|
11052
|
+
)
|
|
11053
|
+
] }),
|
|
11054
|
+
/* @__PURE__ */ jsx(
|
|
11055
|
+
AppHubCatalog,
|
|
11056
|
+
{
|
|
11057
|
+
variant: "panel",
|
|
11058
|
+
onActivate: () => onOpenChange(false),
|
|
11059
|
+
...catalog
|
|
11060
|
+
}
|
|
11061
|
+
)
|
|
11062
|
+
]
|
|
11063
|
+
}
|
|
11064
|
+
)
|
|
11065
|
+
] });
|
|
11066
|
+
}
|
|
11067
|
+
function AppHubRail({
|
|
11068
|
+
id,
|
|
11069
|
+
apps,
|
|
11070
|
+
currentAppId,
|
|
11071
|
+
open,
|
|
11072
|
+
onHide,
|
|
11073
|
+
railRef,
|
|
11074
|
+
panel
|
|
11075
|
+
}) {
|
|
11076
|
+
const { labels } = useAppHubContext();
|
|
11077
|
+
const onKeyDown = useArrowNavigation(railRef);
|
|
11078
|
+
return /* @__PURE__ */ jsxs(
|
|
11079
|
+
"div",
|
|
11080
|
+
{
|
|
11081
|
+
ref: railRef,
|
|
11082
|
+
id,
|
|
11083
|
+
"data-slot": "app-hub-rail",
|
|
11084
|
+
"data-state": open ? "open" : "closed",
|
|
11085
|
+
inert: !open,
|
|
11086
|
+
onKeyDown,
|
|
11087
|
+
className: cn(
|
|
11088
|
+
"pointer-events-auto flex flex-col items-center gap-2 px-2.5 py-3",
|
|
11089
|
+
"border-light-200 bg-background/95 shadow-black-card rounded-l-4xl border border-r-0",
|
|
11090
|
+
"transition-transform duration-300 data-[state=closed]:translate-x-[150%] motion-reduce:transition-none"
|
|
11091
|
+
),
|
|
11092
|
+
children: [
|
|
11093
|
+
/* @__PURE__ */ jsx("ul", { role: "list", className: "flex flex-col items-center gap-2", children: apps.map((app) => /* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsx(AppHubTile, { app, current: app.id === currentAppId }) }, app.id)) }),
|
|
11094
|
+
/* @__PURE__ */ jsx("div", { "aria-hidden": "true", className: "bg-light-300 my-0.5 h-px w-6.5" }),
|
|
11095
|
+
panel,
|
|
11096
|
+
/* @__PURE__ */ jsx("div", { "aria-hidden": "true", className: "bg-light-300 my-0.5 h-px w-6.5" }),
|
|
11097
|
+
/* @__PURE__ */ jsxs(Tooltip, { children: [
|
|
11098
|
+
/* @__PURE__ */ jsx(TooltipTrigger, { asChild: true, "aria-describedby": void 0, children: /* @__PURE__ */ jsx(
|
|
11099
|
+
"button",
|
|
11100
|
+
{
|
|
11101
|
+
type: "button",
|
|
11102
|
+
"aria-label": labels.hideDock,
|
|
11103
|
+
"aria-expanded": "true",
|
|
11104
|
+
"aria-controls": id,
|
|
11105
|
+
"data-slot": "app-hub-hide",
|
|
11106
|
+
"data-app-hub-item": "",
|
|
11107
|
+
onClick: onHide,
|
|
11108
|
+
className: cn(
|
|
11109
|
+
"text-main-grey hover:bg-light-200 hover:text-main-blue-primary flex size-8 items-center justify-center rounded-xl [&_svg]:size-4",
|
|
11110
|
+
FOCUS_RING
|
|
11111
|
+
),
|
|
11112
|
+
children: /* @__PURE__ */ jsx(PanelRightClose, { "aria-hidden": "true" })
|
|
11113
|
+
}
|
|
11114
|
+
) }),
|
|
11115
|
+
/* @__PURE__ */ jsx(
|
|
11116
|
+
TooltipContent,
|
|
11117
|
+
{
|
|
11118
|
+
"aria-hidden": "true",
|
|
11119
|
+
side: "left",
|
|
11120
|
+
variant: "primary",
|
|
11121
|
+
size: "sm",
|
|
11122
|
+
className: TOOLTIP_CLASS,
|
|
11123
|
+
children: labels.hideDock
|
|
11124
|
+
}
|
|
11125
|
+
)
|
|
11126
|
+
] })
|
|
11127
|
+
]
|
|
11128
|
+
}
|
|
11129
|
+
);
|
|
11130
|
+
}
|
|
11131
|
+
function AppHubTab({ railId, open, pending, onShow, tabRef }) {
|
|
11132
|
+
const { labels } = useAppHubContext();
|
|
11133
|
+
const name = [`${labels.tab}, ${labels.showDock}`, pending > 0 && labels.pending(pending)].filter(Boolean).join(", ");
|
|
11134
|
+
return /* @__PURE__ */ jsx(
|
|
11135
|
+
"div",
|
|
11136
|
+
{
|
|
11137
|
+
"data-slot": "app-hub-tab-wrapper",
|
|
11138
|
+
"data-state": open ? "open" : "closed",
|
|
11139
|
+
inert: open,
|
|
11140
|
+
className: "pointer-events-auto transition-transform duration-300 data-[state=open]:translate-x-[150%] motion-reduce:transition-none",
|
|
11141
|
+
children: /* @__PURE__ */ jsxs(
|
|
11142
|
+
"button",
|
|
11143
|
+
{
|
|
11144
|
+
ref: tabRef,
|
|
11145
|
+
type: "button",
|
|
11146
|
+
"aria-label": name,
|
|
11147
|
+
"aria-expanded": "false",
|
|
11148
|
+
"aria-controls": railId,
|
|
11149
|
+
"data-slot": "app-hub-tab",
|
|
11150
|
+
"data-app-hub-entry": "",
|
|
11151
|
+
onClick: onShow,
|
|
11152
|
+
className: cn(
|
|
11153
|
+
"border-light-300 bg-background/95 text-main-blue-primary flex w-10 flex-col items-center gap-2 rounded-l-2xl border border-r-0 py-3.5",
|
|
11154
|
+
FOCUS_RING
|
|
11155
|
+
),
|
|
11156
|
+
children: [
|
|
11157
|
+
/* @__PURE__ */ jsx(LayoutGrid, { "aria-hidden": "true", className: "size-4.5" }),
|
|
11158
|
+
/* @__PURE__ */ jsx(
|
|
11159
|
+
"span",
|
|
11160
|
+
{
|
|
11161
|
+
"aria-hidden": "true",
|
|
11162
|
+
className: "text-main-grey text-[11px] leading-none font-semibold tracking-[0.12em] uppercase [writing-mode:vertical-rl]",
|
|
11163
|
+
children: labels.tab
|
|
11164
|
+
}
|
|
11165
|
+
),
|
|
11166
|
+
pending > 0 && /* @__PURE__ */ jsx(
|
|
11167
|
+
"span",
|
|
11168
|
+
{
|
|
11169
|
+
"aria-hidden": "true",
|
|
11170
|
+
"data-slot": "app-hub-counter",
|
|
11171
|
+
className: "bg-main-orange-primary text-background flex h-4.5 min-w-4.5 items-center justify-center rounded-full px-1.25 text-[11px] leading-none font-bold",
|
|
11172
|
+
children: formatPendingCount(pending)
|
|
11173
|
+
}
|
|
11174
|
+
)
|
|
11175
|
+
]
|
|
11176
|
+
}
|
|
11177
|
+
)
|
|
11178
|
+
}
|
|
11179
|
+
);
|
|
11180
|
+
}
|
|
11181
|
+
function AppHubSheet({ pending, ...catalog }) {
|
|
11182
|
+
const { labels } = useAppHubContext();
|
|
11183
|
+
const [open, setOpen] = useState(false);
|
|
11184
|
+
return /* @__PURE__ */ jsxs(Sheet, { open, onOpenChange: setOpen, children: [
|
|
11185
|
+
/* @__PURE__ */ jsxs(
|
|
11186
|
+
SheetTrigger,
|
|
11187
|
+
{
|
|
11188
|
+
"data-slot": "app-hub-fab",
|
|
11189
|
+
"data-app-hub-entry": "",
|
|
11190
|
+
className: cn(
|
|
11191
|
+
// Bottom-right, in the thumb zone and out of the reading area (the rail centers instead).
|
|
11192
|
+
"bg-main-blue-primary text-main-white shadow-black-button pointer-events-auto mr-4 mb-4 flex h-12 items-center gap-2 self-end rounded-full pr-4.5 pl-4 text-sm font-semibold",
|
|
11193
|
+
FOCUS_RING
|
|
11194
|
+
),
|
|
11195
|
+
children: [
|
|
11196
|
+
/* @__PURE__ */ jsx(LayoutGrid, { "aria-hidden": "true", className: "size-4.5" }),
|
|
11197
|
+
labels.fab,
|
|
11198
|
+
pending > 0 && /* @__PURE__ */ jsxs("span", { className: "sr-only", children: [
|
|
11199
|
+
", ",
|
|
11200
|
+
labels.pending(pending)
|
|
11201
|
+
] }),
|
|
11202
|
+
pending > 0 && /* @__PURE__ */ jsx(
|
|
11203
|
+
"span",
|
|
11204
|
+
{
|
|
11205
|
+
"aria-hidden": "true",
|
|
11206
|
+
"data-slot": "app-hub-counter",
|
|
11207
|
+
className: "bg-main-orange-primary text-background flex h-5 min-w-5 items-center justify-center rounded-full px-1.5 text-[11px] leading-none font-bold",
|
|
11208
|
+
children: formatPendingCount(pending)
|
|
11209
|
+
}
|
|
11210
|
+
)
|
|
11211
|
+
]
|
|
11212
|
+
}
|
|
11213
|
+
),
|
|
11214
|
+
/* @__PURE__ */ jsxs(
|
|
11215
|
+
SheetContent,
|
|
11216
|
+
{
|
|
11217
|
+
side: "bottom",
|
|
11218
|
+
"aria-modal": "true",
|
|
11219
|
+
"aria-describedby": void 0,
|
|
11220
|
+
showCloseButton: false,
|
|
11221
|
+
onOpenAutoFocus: (event) => {
|
|
11222
|
+
event.preventDefault();
|
|
11223
|
+
event.currentTarget.querySelector('[data-slot="app-hub-sheet-close"]')?.focus();
|
|
11224
|
+
},
|
|
11225
|
+
className: "max-h-[85dvh] rounded-t-3xl motion-reduce:transition-none motion-reduce:data-closed:animate-none motion-reduce:data-open:animate-none",
|
|
11226
|
+
children: [
|
|
11227
|
+
/* @__PURE__ */ jsx(
|
|
11228
|
+
"div",
|
|
11229
|
+
{
|
|
11230
|
+
"aria-hidden": "true",
|
|
11231
|
+
className: "bg-light-300 mx-auto mt-2 h-1 w-10 rounded-full"
|
|
11232
|
+
}
|
|
11233
|
+
),
|
|
11234
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between px-4 pt-2", children: [
|
|
11235
|
+
/* @__PURE__ */ jsx(SheetTitle, { className: "font-heading text-main-blue-primary text-xl leading-tight font-bold", children: labels.sheetTitle }),
|
|
11236
|
+
/* @__PURE__ */ jsx(
|
|
11237
|
+
SheetClose,
|
|
11238
|
+
{
|
|
11239
|
+
"data-slot": "app-hub-sheet-close",
|
|
11240
|
+
"aria-label": labels.close,
|
|
11241
|
+
className: cn(
|
|
11242
|
+
"text-main-grey hover:bg-light-200 flex size-11 items-center justify-center rounded-xl [&_svg]:size-5",
|
|
11243
|
+
FOCUS_RING
|
|
11244
|
+
),
|
|
11245
|
+
children: /* @__PURE__ */ jsx(X, { "aria-hidden": "true" })
|
|
11246
|
+
}
|
|
11247
|
+
)
|
|
11248
|
+
] }),
|
|
11249
|
+
/* @__PURE__ */ jsx(AppHubCatalog, { ...catalog, variant: "sheet", onActivate: () => setOpen(false) })
|
|
11250
|
+
]
|
|
11251
|
+
}
|
|
11252
|
+
)
|
|
11253
|
+
] });
|
|
11254
|
+
}
|
|
11255
|
+
var noopSubscribe = () => () => {
|
|
11256
|
+
};
|
|
11257
|
+
var nullSnapshot = () => null;
|
|
11258
|
+
function useAppHubOpen({
|
|
11259
|
+
open: controlledOpen,
|
|
11260
|
+
defaultOpen,
|
|
11261
|
+
onOpenChange,
|
|
11262
|
+
storageKey: storageKey2,
|
|
11263
|
+
layoutDefault
|
|
11264
|
+
}) {
|
|
11265
|
+
const controlled = controlledOpen !== void 0;
|
|
11266
|
+
const persistedKey = !controlled ? storageKey2 : null;
|
|
11267
|
+
const fallback = defaultOpen ?? layoutDefault;
|
|
11268
|
+
const [memoryOpen, setMemoryOpen] = useState(void 0);
|
|
11269
|
+
const [sessionOverride, setSessionOverride] = useState(void 0);
|
|
11270
|
+
const store = useMemo(() => {
|
|
11271
|
+
if (persistedKey === null) return { subscribe: noopSubscribe, getSnapshot: nullSnapshot };
|
|
11272
|
+
const subscribe2 = (notify) => {
|
|
11273
|
+
const onStorage = (event) => {
|
|
11274
|
+
if (event.key === null || event.key === persistedKey) notify();
|
|
11275
|
+
};
|
|
11276
|
+
const onChange = (event) => {
|
|
11277
|
+
if (event.detail?.key === persistedKey) notify();
|
|
11278
|
+
};
|
|
11279
|
+
window.addEventListener("storage", onStorage);
|
|
11280
|
+
window.addEventListener(OPEN_CHANGE_EVENT, onChange);
|
|
11281
|
+
return () => {
|
|
11282
|
+
window.removeEventListener("storage", onStorage);
|
|
11283
|
+
window.removeEventListener(OPEN_CHANGE_EVENT, onChange);
|
|
11284
|
+
};
|
|
11285
|
+
};
|
|
11286
|
+
const getSnapshot2 = () => {
|
|
11287
|
+
try {
|
|
11288
|
+
const value = window.localStorage.getItem(persistedKey);
|
|
11289
|
+
return value === "true" ? true : value === "false" ? false : null;
|
|
11290
|
+
} catch {
|
|
11291
|
+
return null;
|
|
11292
|
+
}
|
|
11293
|
+
};
|
|
11294
|
+
return { subscribe: subscribe2, getSnapshot: getSnapshot2 };
|
|
11295
|
+
}, [persistedKey]);
|
|
11296
|
+
const stored = useSyncExternalStore(store.subscribe, store.getSnapshot, nullSnapshot);
|
|
11297
|
+
const setOpen = useCallback(
|
|
11298
|
+
(next) => {
|
|
11299
|
+
onOpenChange?.(next);
|
|
11300
|
+
if (controlled) return;
|
|
11301
|
+
if (storageKey2 === null) {
|
|
11302
|
+
setMemoryOpen(next);
|
|
11303
|
+
return;
|
|
11304
|
+
}
|
|
11305
|
+
try {
|
|
11306
|
+
window.localStorage.setItem(storageKey2, String(next));
|
|
11307
|
+
setSessionOverride(void 0);
|
|
11308
|
+
} catch {
|
|
11309
|
+
setSessionOverride(next);
|
|
11310
|
+
}
|
|
11311
|
+
window.dispatchEvent(
|
|
11312
|
+
new CustomEvent(OPEN_CHANGE_EVENT, { detail: { key: storageKey2 } })
|
|
11313
|
+
);
|
|
11314
|
+
},
|
|
11315
|
+
[controlled, onOpenChange, storageKey2]
|
|
11316
|
+
);
|
|
11317
|
+
let open;
|
|
11318
|
+
if (controlled) open = controlledOpen;
|
|
11319
|
+
else if (storageKey2 === null) open = memoryOpen ?? fallback;
|
|
11320
|
+
else open = sessionOverride ?? stored ?? fallback;
|
|
11321
|
+
return { open, setOpen };
|
|
11322
|
+
}
|
|
11323
|
+
var DESKTOP_QUERY = "(min-width: 1024px)";
|
|
11324
|
+
var TABLET_QUERY = "(min-width: 768px)";
|
|
11325
|
+
function hasMatchMedia() {
|
|
11326
|
+
return typeof window !== "undefined" && typeof window.matchMedia === "function";
|
|
11327
|
+
}
|
|
11328
|
+
function subscribe(notify) {
|
|
11329
|
+
if (!hasMatchMedia()) return () => {
|
|
11330
|
+
};
|
|
11331
|
+
const lists = [DESKTOP_QUERY, TABLET_QUERY].map((query) => window.matchMedia(query));
|
|
11332
|
+
lists.forEach((list) => list.addEventListener("change", notify));
|
|
11333
|
+
return () => lists.forEach((list) => list.removeEventListener("change", notify));
|
|
11334
|
+
}
|
|
11335
|
+
function getSnapshot() {
|
|
11336
|
+
if (!hasMatchMedia()) return "desktop";
|
|
11337
|
+
if (window.matchMedia(DESKTOP_QUERY).matches) return "desktop";
|
|
11338
|
+
return window.matchMedia(TABLET_QUERY).matches ? "tablet" : "mobile";
|
|
11339
|
+
}
|
|
11340
|
+
var getServerSnapshot = () => "desktop";
|
|
11341
|
+
function useAppHubViewport() {
|
|
11342
|
+
return useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot);
|
|
11343
|
+
}
|
|
11344
|
+
function AppHub({
|
|
11345
|
+
apps,
|
|
11346
|
+
appName,
|
|
11347
|
+
currentAppId,
|
|
11348
|
+
catalogStatus = "ready",
|
|
11349
|
+
onRetry,
|
|
11350
|
+
directoryHref,
|
|
11351
|
+
layout = "auto",
|
|
11352
|
+
searchThreshold = 7,
|
|
11353
|
+
position = "fixed",
|
|
11354
|
+
open: openProp,
|
|
11355
|
+
defaultOpen,
|
|
11356
|
+
onOpenChange,
|
|
11357
|
+
storageKey: storageKey2 = DEFAULT_STORAGE_KEY,
|
|
11358
|
+
onNavigate,
|
|
11359
|
+
renderLink,
|
|
11360
|
+
labels: labelOverrides,
|
|
11361
|
+
id = DEFAULT_APP_HUB_ID,
|
|
11362
|
+
className
|
|
11363
|
+
}) {
|
|
11364
|
+
const merged = mergeLabels(DEFAULT_APP_HUB_LABELS, labelOverrides);
|
|
11365
|
+
const [stableLabels, setStableLabels] = useState(merged);
|
|
11366
|
+
const labels = areLabelsEqual(stableLabels, merged) ? stableLabels : merged;
|
|
11367
|
+
if (labels !== stableLabels) setStableLabels(labels);
|
|
11368
|
+
const viewport = useAppHubViewport();
|
|
11369
|
+
const family = layout === "auto" ? viewport === "mobile" ? "sheet" : "dock" : layout;
|
|
11370
|
+
const { open, setOpen } = useAppHubOpen({
|
|
11371
|
+
open: openProp,
|
|
11372
|
+
defaultOpen,
|
|
11373
|
+
onOpenChange,
|
|
11374
|
+
storageKey: storageKey2,
|
|
11375
|
+
// Tablet starts collapsed only when the layout follows the viewport.
|
|
11376
|
+
layoutDefault: !(layout === "auto" && viewport === "tablet")
|
|
11377
|
+
});
|
|
11378
|
+
const [panelOpen, setPanelOpen] = useState(false);
|
|
11379
|
+
if (!open && panelOpen) setPanelOpen(false);
|
|
11380
|
+
const [announcement, setAnnouncement] = useState("");
|
|
11381
|
+
const [, requestRender] = useReducer((count) => count + 1, 0);
|
|
11382
|
+
const previousOpenRef = useRef(open);
|
|
11383
|
+
const railRef = useRef(null);
|
|
11384
|
+
const tabRef = useRef(null);
|
|
11385
|
+
const focusIntentRef = useRef(null);
|
|
11386
|
+
const railId = `${id}-rail`;
|
|
11387
|
+
useLayoutEffect(() => {
|
|
11388
|
+
const intent = focusIntentRef.current;
|
|
11389
|
+
focusIntentRef.current = null;
|
|
11390
|
+
if (previousOpenRef.current === open) return;
|
|
11391
|
+
previousOpenRef.current = open;
|
|
11392
|
+
setAnnouncement(intent === open ? open ? labels.dockShown : labels.dockHidden : "");
|
|
11393
|
+
const entry = open ? getEntryElement(railRef.current) : tabRef.current;
|
|
11394
|
+
if (intent === open) {
|
|
11395
|
+
entry?.focus();
|
|
11396
|
+
return;
|
|
11397
|
+
}
|
|
11398
|
+
const active = document.activeElement;
|
|
11399
|
+
const stranded = open ? [tabRef.current] : [railRef.current, document.getElementById(`${id}-panel`)];
|
|
11400
|
+
if (active && stranded.some((element) => element?.contains(active))) entry?.focus();
|
|
11401
|
+
});
|
|
11402
|
+
function toggle(next) {
|
|
11403
|
+
focusIntentRef.current = next;
|
|
11404
|
+
setOpen(next);
|
|
11405
|
+
requestRender();
|
|
11406
|
+
}
|
|
11407
|
+
const context = useMemo(
|
|
11408
|
+
() => ({ labels, appName, renderLink, onNavigate }),
|
|
11409
|
+
[labels, appName, renderLink, onNavigate]
|
|
11410
|
+
);
|
|
11411
|
+
return /* @__PURE__ */ jsx(AppHubContext, { value: context, children: /* @__PURE__ */ jsxs(
|
|
11412
|
+
"nav",
|
|
11413
|
+
{
|
|
11414
|
+
id,
|
|
11415
|
+
"aria-label": labels.navigation,
|
|
11416
|
+
"data-slot": "app-hub",
|
|
11417
|
+
"data-layout": family,
|
|
11418
|
+
"data-state": open ? "open" : "closed",
|
|
11419
|
+
"data-position": position,
|
|
11420
|
+
className: cn(
|
|
11421
|
+
// `overflow-x-clip` keeps the slid-out form from creating scrollable overflow
|
|
11422
|
+
// (a scrollIntoView would otherwise shift the host); `pl-8` leaves room for the shadow.
|
|
11423
|
+
"pointer-events-none inset-y-0 right-0 z-40 grid items-center justify-items-end overflow-x-clip pl-8 *:col-start-1 *:row-start-1",
|
|
11424
|
+
position === "fixed" ? "fixed" : "absolute",
|
|
11425
|
+
className
|
|
11426
|
+
),
|
|
11427
|
+
children: [
|
|
11428
|
+
family === "sheet" ? /* @__PURE__ */ jsx(
|
|
11429
|
+
AppHubSheet,
|
|
11430
|
+
{
|
|
11431
|
+
apps,
|
|
11432
|
+
currentAppId,
|
|
11433
|
+
status: catalogStatus,
|
|
11434
|
+
onRetry,
|
|
11435
|
+
directoryHref,
|
|
11436
|
+
searchThreshold,
|
|
11437
|
+
pending: sumPending(apps)
|
|
11438
|
+
}
|
|
11439
|
+
) : /* @__PURE__ */ jsxs(Fragment$1, { children: [
|
|
11440
|
+
/* @__PURE__ */ jsx(
|
|
11441
|
+
AppHubRail,
|
|
11442
|
+
{
|
|
11443
|
+
id: railId,
|
|
11444
|
+
apps: apps.filter((app) => app.inRail && !app.locked),
|
|
11445
|
+
currentAppId,
|
|
11446
|
+
open,
|
|
11447
|
+
onHide: () => toggle(false),
|
|
11448
|
+
railRef,
|
|
11449
|
+
panel: /* @__PURE__ */ jsx(
|
|
11450
|
+
AppHubPanel,
|
|
11451
|
+
{
|
|
11452
|
+
id: `${id}-panel`,
|
|
11453
|
+
open: panelOpen,
|
|
11454
|
+
onOpenChange: setPanelOpen,
|
|
11455
|
+
onCloseAutoFocus: (event) => {
|
|
11456
|
+
if (open) return;
|
|
11457
|
+
event.preventDefault();
|
|
11458
|
+
const active = document.activeElement;
|
|
11459
|
+
if (!active || active === document.body)
|
|
11460
|
+
tabRef.current?.focus();
|
|
11461
|
+
},
|
|
11462
|
+
apps,
|
|
11463
|
+
currentAppId,
|
|
11464
|
+
status: catalogStatus,
|
|
11465
|
+
onRetry,
|
|
11466
|
+
directoryHref,
|
|
11467
|
+
searchThreshold
|
|
11468
|
+
}
|
|
11469
|
+
)
|
|
11470
|
+
}
|
|
11471
|
+
),
|
|
11472
|
+
/* @__PURE__ */ jsx(
|
|
11473
|
+
AppHubTab,
|
|
11474
|
+
{
|
|
11475
|
+
railId,
|
|
11476
|
+
open,
|
|
11477
|
+
pending: sumPending(apps),
|
|
11478
|
+
onShow: () => toggle(true),
|
|
11479
|
+
tabRef
|
|
11480
|
+
}
|
|
11481
|
+
)
|
|
11482
|
+
] }),
|
|
11483
|
+
/* @__PURE__ */ jsx(
|
|
11484
|
+
"div",
|
|
11485
|
+
{
|
|
11486
|
+
role: "status",
|
|
11487
|
+
"aria-live": "polite",
|
|
11488
|
+
"aria-atomic": "true",
|
|
11489
|
+
"data-slot": "app-hub-live",
|
|
11490
|
+
className: "sr-only",
|
|
11491
|
+
children: announcement
|
|
11492
|
+
}
|
|
11493
|
+
)
|
|
11494
|
+
]
|
|
11495
|
+
}
|
|
11496
|
+
) });
|
|
11497
|
+
}
|
|
11498
|
+
function getLiveEntry(root) {
|
|
11499
|
+
const live = [...root?.querySelectorAll("[data-slot]") ?? []].find(
|
|
11500
|
+
(region) => !region.closest("[inert]") && getEntryElement(region) !== null
|
|
11501
|
+
);
|
|
11502
|
+
return getEntryElement(live ?? root);
|
|
11503
|
+
}
|
|
11504
|
+
function AppHubSkipLink({
|
|
11505
|
+
targetId = DEFAULT_APP_HUB_ID,
|
|
11506
|
+
children = "Skip to applications",
|
|
11507
|
+
className,
|
|
11508
|
+
onClick,
|
|
11509
|
+
...props
|
|
11510
|
+
}) {
|
|
11511
|
+
function handleClick(event) {
|
|
11512
|
+
onClick?.(event);
|
|
11513
|
+
if (event.defaultPrevented) return;
|
|
11514
|
+
const entry = getLiveEntry(document.getElementById(targetId));
|
|
11515
|
+
if (!entry) return;
|
|
11516
|
+
event.preventDefault();
|
|
11517
|
+
entry.focus();
|
|
11518
|
+
}
|
|
11519
|
+
return /* @__PURE__ */ jsx(
|
|
11520
|
+
"a",
|
|
11521
|
+
{
|
|
11522
|
+
...props,
|
|
11523
|
+
href: `#${targetId}`,
|
|
11524
|
+
"data-slot": "app-hub-skip-link",
|
|
11525
|
+
onClick: handleClick,
|
|
11526
|
+
className: cn(
|
|
11527
|
+
"bg-main-blue-primary text-main-white sr-only rounded-md px-4 py-2 text-sm font-semibold",
|
|
11528
|
+
"focus:not-sr-only focus:fixed focus:top-2 focus:left-2 focus:z-50",
|
|
11529
|
+
"focus-visible:outline-main-orange-primary outline-hidden focus-visible:outline-2 focus-visible:outline-offset-2",
|
|
11530
|
+
className
|
|
11531
|
+
),
|
|
11532
|
+
children
|
|
11533
|
+
}
|
|
11534
|
+
);
|
|
11535
|
+
}
|
|
9177
11536
|
var DrawerContext = createContext({ isOpen: false, modal: true });
|
|
9178
11537
|
function Drawer({ children, open, onOpenChange, modal = true, ...props }) {
|
|
9179
11538
|
const [internalOpen, setInternalOpen] = useState(props.defaultOpen ?? false);
|
|
@@ -9602,6 +11961,6 @@ function TCCEProvider({
|
|
|
9602
11961
|
return /* @__PURE__ */ jsx(TooltipProvider, { delayDuration: tooltipDelayDuration, children: /* @__PURE__ */ jsx(AbilityProvider, { permissions, children: /* @__PURE__ */ jsx(Toaster, { position: toasterPosition, children }) }) });
|
|
9603
11962
|
}
|
|
9604
11963
|
|
|
9605
|
-
export { AbilityProvider, Accordion, AccordionContent, AccordionItem, AccordionTrigger, Alert, AlertDescription, AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogMedia, AlertDialogOverlay, AlertDialogPortal, AlertDialogTitle, AlertDialogTrigger, AlertTitle, AppShell, Avatar, AvatarBadge, AvatarFallback, AvatarGroup, AvatarGroupCount, AvatarImage, Badge2 as Badge, BottomNav, BottomNavTabs, BottomNavTabsContent, BottomNavTabsList, Breadcrumb, BreadcrumbEllipsis, BreadcrumbItem, BreadcrumbLink, BreadcrumbList, BreadcrumbPage, BreadcrumbSeparator, Button, CONFETTI_COLORS, Can, CanNot, Card, CardAction, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, Carousel, CarouselContent, CarouselItem, CarouselNext, CarouselPrevious, Checkbox, ColorPicker, ColorPickerAlpha, ColorPickerEyeDropper, ColorPickerFormat, ColorPickerHue, ColorPickerOutput, ColorPickerPresets, ColorPickerSelection, Combobox, DataTable, DataTableCardView, DataTableColumnFilter, DataTableFooter, DataTableHeaderCell, DataTableRowActions, DataTableSearch, DataTableSkeleton, DataTableToolbar, DatePicker, Dialog, DialogBody, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogOverlay, DialogPortal, DialogTitle, DialogTrigger, Drawer, DrawerBody, DrawerClose, DrawerContent, DrawerDescription, DrawerFooter, DrawerHeader, DrawerOverlay, DrawerPortal, DrawerTitle, DrawerTrigger, DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuLabel, DropdownMenuPortal, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuSeparator, DropdownMenuShortcut, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger, DropdownMenuTrigger, Dropzone, DropzoneContent, DropzoneEmptyState, Empty, EmptyContent, EmptyDescription, EmptyHeader, EmptyMedia, EmptyTitle, EventCalendar, Field, FieldDescription, FieldError, FieldGroup, FieldLabel, FilterOption, Input, Item, ItemActions, ItemContent, ItemDescription, ItemFooter, ItemGroup, ItemHeader, ItemMedia, ItemSeparator, ItemTitle, KNOWN_PERMISSIONS, Label, Link, Logo, MiniCalendar, MiniCalendarDay, MiniCalendarDays, MiniCalendarNavigation, Navbar, NavigationMenu, NavigationMenuContent, NavigationMenuIndicator, NavigationMenuItem, NavigationMenuLink, NavigationMenuList, NavigationMenuTrigger, NavigationMenuViewport, Pagination, PaginationContent, PaginationEllipsis, PaginationItem, PaginationLink, PaginationNext, PaginationPrevious, PhoneField, PinInput, Popover, PopoverAnchor, PopoverContent, PopoverDescription, PopoverHeader, PopoverTitle, PopoverTrigger, Progress, RadioGroup, RadioGroupItem, SegmentedControl, SegmentedControlItem, Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectScrollDownButton, SelectScrollUpButton, SelectSeparator, SelectTrigger, SelectValue, Separator, Sheet, SheetClose, SheetContent, SheetDescription, SheetFooter, SheetHeader, SheetTitle, SheetTrigger, Sidebar, SidebarContent, SidebarFooter, SidebarGroup, SidebarGroupAction, SidebarGroupContent, SidebarGroupLabel, SidebarHeader, SidebarInput, SidebarInset, SidebarMenu, SidebarMenuAction, SidebarMenuBadge, SidebarMenuButton, SidebarMenuItem, SidebarMenuSkeleton, SidebarMenuSub, SidebarMenuSubButton, SidebarMenuSubItem, SidebarProvider, SidebarRail, SidebarSeparator, SidebarTrigger, SignaturePad, Skeleton, Slider, Spinner, SpinnerDots, SpinnerLoader, Status, StatusIndicator, StatusLabel, Switch, TCCEProvider, TEXT_SIZES, TEXT_WEIGHTS, Table, TableBody, TableCaption, TableCell, TableFooter, TableHead, TableHeader, TablePagination, TableRow, Tabs, TabsContent, TabsList, TabsTrigger, Text, Textarea, Toaster, Toggle, ToggleGroup2 as ToggleGroup, ToggleGroupItem, Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, alertVariants, animation, avatarVariants, badgeVariants2 as badgeVariants, buttonVariants, columnFiltersToArrayQueryParams, columnFiltersToJsonString, columnFiltersToQueryParams, columnFiltersToRecord, createBackendParams, createBackendParamsWithFilters, duration, ease, fadeIn, fireConfetti, getActiveFilterCount, getActiveFilterValues, getColumnValueCounts, getUniqueColumnValues, hasActiveFilters, linkVariants, logos, matchPermission, mergeFilters, multiSelectFilter, navigationMenuTriggerStyle, parseJsonToFilters, parseQueryParamsToFilters, progressIndicatorVariants, progressVariants, resetConfetti, scaleIn, searchInArray, skeletonVariants, slideRight, slideUp, sortRecordToSortingState, sortingStateToJsonString, sortingStateToRecord, spinnerDotsVariants, spinnerLoaderVariants, spinnerVariants, spring, stagger, toggleVariants, tooltipContentVariants, transition, useAbility, useAbilityStatus, useAppForm, useCan, useCarousel, useColorPicker, useColumnFilter, useSidebar, useTableContext, useTableData, useTableFiltering, useTableGlobalFilter, useTableSizing, useTableSorting, useTableVisibility, useToast, validatePhone };
|
|
11964
|
+
export { AbilityProvider, Accordion, AccordionContent, AccordionItem, AccordionTrigger, Alert, AlertDescription, AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogMedia, AlertDialogOverlay, AlertDialogPortal, AlertDialogTitle, AlertDialogTrigger, AlertTitle, AppHub, AppHubSkipLink, AppShell, Avatar, AvatarBadge, AvatarFallback, AvatarGroup, AvatarGroupCount, AvatarImage, Badge2 as Badge, BottomNav, BottomNavTabs, BottomNavTabsContent, BottomNavTabsList, Breadcrumb, BreadcrumbEllipsis, BreadcrumbItem, BreadcrumbLink, BreadcrumbList, BreadcrumbPage, BreadcrumbSeparator, Button, CONFETTI_COLORS, Can, CanNot, Card, CardAction, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, Carousel, CarouselContent, CarouselItem, CarouselNext, CarouselPrevious, Checkbox, ColorPicker, ColorPickerAlpha, ColorPickerEyeDropper, ColorPickerFormat, ColorPickerHue, ColorPickerOutput, ColorPickerPresets, ColorPickerSelection, Combobox, DataTable, DataTableCardView, DataTableColumnFilter, DataTableFooter, DataTableHeaderCell, DataTableRowActions, DataTableSearch, DataTableSkeleton, DataTableToolbar, DatePicker, Dialog, DialogBody, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogOverlay, DialogPortal, DialogTitle, DialogTrigger, Drawer, DrawerBody, DrawerClose, DrawerContent, DrawerDescription, DrawerFooter, DrawerHeader, DrawerOverlay, DrawerPortal, DrawerTitle, DrawerTrigger, DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuLabel, DropdownMenuPortal, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuSeparator, DropdownMenuShortcut, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger, DropdownMenuTrigger, Dropzone, DropzoneContent, DropzoneEmptyState, Empty, EmptyContent, EmptyDescription, EmptyHeader, EmptyMedia, EmptyTitle, EventCalendar, Field, FieldDescription, FieldError, FieldGroup, FieldLabel, FilterOption, FloatingSidebar, Input, Item, ItemActions, ItemContent, ItemDescription, ItemFooter, ItemGroup, ItemHeader, ItemMedia, ItemSeparator, ItemTitle, KNOWN_PERMISSIONS, Label, Link, Logo, MiniCalendar, MiniCalendarDay, MiniCalendarDays, MiniCalendarNavigation, Navbar, NavigationMenu, NavigationMenuContent, NavigationMenuIndicator, NavigationMenuItem, NavigationMenuLink, NavigationMenuList, NavigationMenuTrigger, NavigationMenuViewport, Pagination, PaginationContent, PaginationEllipsis, PaginationItem, PaginationLink, PaginationNext, PaginationPrevious, PhoneField, PinInput, Popover, PopoverAnchor, PopoverContent, PopoverDescription, PopoverHeader, PopoverTitle, PopoverTrigger, Progress, RadioGroup, RadioGroupItem, SegmentedControl, SegmentedControlItem, Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectScrollDownButton, SelectScrollUpButton, SelectSeparator, SelectTrigger, SelectValue, Separator, Sheet, SheetClose, SheetContent, SheetDescription, SheetFooter, SheetHeader, SheetTitle, SheetTrigger, Sidebar, SidebarContent, SidebarFooter, SidebarGroup, SidebarGroupAction, SidebarGroupContent, SidebarGroupLabel, SidebarHeader, SidebarInput, SidebarInset, SidebarMenu, SidebarMenuAction, SidebarMenuBadge, SidebarMenuButton, SidebarMenuItem, SidebarMenuSkeleton, SidebarMenuSub, SidebarMenuSubButton, SidebarMenuSubItem, SidebarProvider, SidebarRail, SidebarSeparator, SidebarTrigger, SignaturePad, Skeleton, Slider, Spinner, SpinnerDots, SpinnerLoader, Status, StatusIndicator, StatusLabel, Switch, TCCEProvider, TEXT_SIZES, TEXT_WEIGHTS, Table, TableBody, TableCaption, TableCell, TableFooter, TableHead, TableHeader, TablePagination, TableRow, Tabs, TabsContent, TabsList, TabsTrigger, Text, Textarea, Toaster, Toggle, ToggleGroup2 as ToggleGroup, ToggleGroupItem, Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, alertVariants, animation, avatarVariants, badgeVariants2 as badgeVariants, buttonVariants, columnFiltersToArrayQueryParams, columnFiltersToJsonString, columnFiltersToQueryParams, columnFiltersToRecord, createBackendParams, createBackendParamsWithFilters, duration, ease, fadeIn, fireConfetti, getActiveFilterCount, getActiveFilterValues, getColumnValueCounts, getUniqueColumnValues, hasActiveFilters, linkVariants, logos, matchPermission, mergeFilters, multiSelectFilter, navigationMenuTriggerStyle, parseJsonToFilters, parseQueryParamsToFilters, progressIndicatorVariants, progressVariants, resetConfetti, scaleIn, searchInArray, skeletonVariants, slideRight, slideUp, sortRecordToSortingState, sortingStateToJsonString, sortingStateToRecord, spinnerDotsVariants, spinnerLoaderVariants, spinnerVariants, spring, stagger, toggleVariants, tooltipContentVariants, transition, useAbility, useAbilityStatus, useAppForm, useCan, useCarousel, useColorPicker, useColumnFilter, useSidebar, useTableContext, useTableData, useTableFiltering, useTableGlobalFilter, useTableSizing, useTableSorting, useTableVisibility, useToast, validatePhone };
|
|
9606
11965
|
//# sourceMappingURL=main.js.map
|
|
9607
11966
|
//# sourceMappingURL=main.js.map
|