@salemcobalt/ui 0.2.0-rc.10 → 0.2.0-rc.12

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/index.js CHANGED
@@ -2124,7 +2124,10 @@ function Skeleton({
2124
2124
  }
2125
2125
 
2126
2126
  // src/components/FileInput/FileInput.tsx
2127
- import { useEffect as useEffect3, useId as useId3, useRef as useRef3, useState as useState5 } from "react";
2127
+ import { useEffect as useEffect4, useId as useId3, useRef as useRef4, useState as useState6 } from "react";
2128
+
2129
+ // src/components/FilePreviewDialog/FilePreviewDialog.tsx
2130
+ import { useEffect as useEffect3, useRef as useRef3, useState as useState5 } from "react";
2128
2131
 
2129
2132
  // src/components/FileInput/accept.ts
2130
2133
  function parseAccept(accept) {
@@ -2155,6 +2158,14 @@ function formatSize(bytes) {
2155
2158
  const value = bytes / base;
2156
2159
  return `${Number.isInteger(value) ? value : Number(value.toFixed(1))} ${unit}`;
2157
2160
  }
2161
+ function formatListSize(bytes) {
2162
+ const n = Math.round(Math.abs(bytes));
2163
+ const trim = (value, decimals) => value.toFixed(decimals).replace(/\.0+$/, "");
2164
+ if (n < 1e3) return `${n} bytes`;
2165
+ if (n < 1e6) return `${Math.floor(n / 1e3)} KB`;
2166
+ if (n < 1e9) return `${trim(n / 1e6, 1)} MB`;
2167
+ return `${trim(n / 1e9, 2)} GB`;
2168
+ }
2158
2169
  var MIME_NAMES = {
2159
2170
  "application/pdf": "PDF",
2160
2171
  "application/msword": "DOC",
@@ -2192,9 +2203,167 @@ function escapeHtml(text) {
2192
2203
  (ch) => ({ "&": "&amp;", "<": "&lt;", ">": "&gt;", '"': "&quot;", "'": "&#39;" })[ch]
2193
2204
  );
2194
2205
  }
2206
+ var extensionOf = (name) => {
2207
+ const dot = name.lastIndexOf(".");
2208
+ return dot > 0 ? name.slice(dot + 1).toLowerCase() : "";
2209
+ };
2210
+ var TEXT_EXTENSIONS = /* @__PURE__ */ new Set(["txt", "text", "md", "markdown"]);
2211
+ var TEXT_TYPES = /* @__PURE__ */ new Set(["text/plain", "text/markdown", "text/x-markdown"]);
2212
+ var IMAGE_EXTENSIONS = /* @__PURE__ */ new Set(["png", "jpg", "jpeg", "gif", "webp", "avif", "svg", "bmp"]);
2213
+ var UNDRAWABLE_IMAGE_TYPES = /* @__PURE__ */ new Set(["image/heic", "image/heif", "image/tiff"]);
2214
+ function previewKind(file) {
2215
+ const ext = extensionOf(file.name);
2216
+ const type = file.type.toLowerCase();
2217
+ if (ext === "pdf" || type === "application/pdf") return "pdf";
2218
+ if (TEXT_EXTENSIONS.has(ext) || TEXT_TYPES.has(type)) return "text";
2219
+ if (UNDRAWABLE_IMAGE_TYPES.has(type)) return null;
2220
+ if (IMAGE_EXTENSIONS.has(ext) || type.startsWith("image/")) return "image";
2221
+ return null;
2222
+ }
2223
+ function fileTypeLabel(file) {
2224
+ const ext = extensionOf(file.name);
2225
+ if (ext) return ext.toUpperCase();
2226
+ return MIME_NAMES[file.type.toLowerCase()] ?? (file.type.split("/").pop()?.toUpperCase() || "File");
2227
+ }
2228
+
2229
+ // src/utils/useMediaQuery.ts
2230
+ import { useCallback, useSyncExternalStore } from "react";
2231
+ function useMediaQuery(query) {
2232
+ const subscribe = useCallback(
2233
+ (onChange) => {
2234
+ if (typeof window === "undefined" || typeof window.matchMedia !== "function") return () => {
2235
+ };
2236
+ const list = window.matchMedia(query);
2237
+ list.addEventListener?.("change", onChange);
2238
+ return () => list.removeEventListener?.("change", onChange);
2239
+ },
2240
+ [query]
2241
+ );
2242
+ return useSyncExternalStore(
2243
+ subscribe,
2244
+ () => typeof window.matchMedia === "function" && window.matchMedia(query).matches,
2245
+ () => false
2246
+ );
2247
+ }
2248
+
2249
+ // src/components/FilePreviewDialog/FilePreviewDialog.tsx
2250
+ import { Fragment as Fragment8, jsx as jsx29, jsxs as jsxs24 } from "react/jsx-runtime";
2251
+ var TEXT_LIMIT = 1e6;
2252
+ var NARROW = "(max-width: 48rem)";
2253
+ function FilePreviewDialog({ file, open, onOpenChange, finalFocus }) {
2254
+ const kind = file ? previewKind(file) : null;
2255
+ const narrow = useMediaQuery(NARROW);
2256
+ const pdfInTab = kind === "pdf" && narrow;
2257
+ return /* @__PURE__ */ jsx29(Dialog.Root, { open, onOpenChange, children: /* @__PURE__ */ jsxs24(
2258
+ Dialog.Content,
2259
+ {
2260
+ size: "lg",
2261
+ className: "sc-file-preview",
2262
+ "data-kind": kind ?? "none",
2263
+ finalFocus,
2264
+ children: [
2265
+ /* @__PURE__ */ jsx29(Dialog.Close, {}),
2266
+ file ? /* @__PURE__ */ jsxs24(Fragment8, { children: [
2267
+ /* @__PURE__ */ jsx29(Dialog.Title, { children: file.name }),
2268
+ /* @__PURE__ */ jsxs24(Dialog.Description, { children: [
2269
+ fileTypeLabel(file),
2270
+ ", ",
2271
+ formatListSize(file.size)
2272
+ ] }),
2273
+ /* @__PURE__ */ jsx29(Dialog.Body, { children: /* @__PURE__ */ jsx29(PreviewBody, { file, kind, open, pdfInTab }) })
2274
+ ] }) : /* @__PURE__ */ jsx29(Dialog.Title, { children: "Preview" })
2275
+ ]
2276
+ }
2277
+ ) });
2278
+ }
2279
+ function PreviewBody({
2280
+ file,
2281
+ kind,
2282
+ open,
2283
+ pdfInTab
2284
+ }) {
2285
+ const frameRef = useRef3(null);
2286
+ const imageRef = useRef3(null);
2287
+ const linkRef = useRef3(null);
2288
+ const [text, setText] = useState5(null);
2289
+ useEffect3(() => {
2290
+ if (!open || kind !== "pdf" && kind !== "image") return void 0;
2291
+ const url = URL.createObjectURL(kind === "pdf" ? new Blob([file], { type: "application/pdf" }) : file);
2292
+ if (frameRef.current) frameRef.current.src = url;
2293
+ if (imageRef.current) imageRef.current.src = url;
2294
+ if (linkRef.current) linkRef.current.href = url;
2295
+ return () => URL.revokeObjectURL(url);
2296
+ }, [open, file, kind, pdfInTab]);
2297
+ useEffect3(() => {
2298
+ if (!open || kind !== "text") return void 0;
2299
+ let cancelled = false;
2300
+ void file.slice(0, TEXT_LIMIT).text().then((value) => {
2301
+ if (!cancelled) setText({ file, value });
2302
+ });
2303
+ return () => {
2304
+ cancelled = true;
2305
+ };
2306
+ }, [open, file, kind]);
2307
+ if (kind === "pdf") {
2308
+ const link = (
2309
+ // eslint-disable-next-line jsx-a11y/anchor-is-valid -- the effect above sets href to the object URL on mount
2310
+ /* @__PURE__ */ jsxs24(
2311
+ "a",
2312
+ {
2313
+ ref: linkRef,
2314
+ className: "sc-button",
2315
+ "data-variant": pdfInTab ? "primary" : "secondary",
2316
+ "data-size": "md",
2317
+ target: "_blank",
2318
+ rel: "noopener",
2319
+ children: [
2320
+ "Open in a new tab",
2321
+ /* @__PURE__ */ jsx29(ExternalLinkIcon, {})
2322
+ ]
2323
+ }
2324
+ )
2325
+ );
2326
+ return pdfInTab ? /* @__PURE__ */ jsxs24("div", { className: "sc-file-preview__tab", children: [
2327
+ /* @__PURE__ */ jsx29("p", { className: "sc-file-preview__note", children: "On a small screen the PDF reads best in its own tab." }),
2328
+ link
2329
+ ] }) : /* @__PURE__ */ jsxs24(Fragment8, { children: [
2330
+ /* @__PURE__ */ jsx29("iframe", { ref: frameRef, className: "sc-file-preview__frame", title: `Preview of ${file.name}` }),
2331
+ /* @__PURE__ */ jsx29("div", { className: "sc-file-preview__actions", children: link })
2332
+ ] });
2333
+ }
2334
+ if (kind === "text") {
2335
+ const value = text?.file === file ? text.value : null;
2336
+ return /* @__PURE__ */ jsxs24(Fragment8, { children: [
2337
+ /* @__PURE__ */ jsx29(
2338
+ "pre",
2339
+ {
2340
+ className: "sc-file-preview__text",
2341
+ role: "region",
2342
+ tabIndex: 0,
2343
+ "aria-label": `Contents of ${file.name}`,
2344
+ "aria-busy": value == null || void 0,
2345
+ children: value
2346
+ }
2347
+ ),
2348
+ file.size > TEXT_LIMIT ? /* @__PURE__ */ jsxs24("p", { className: "sc-file-preview__note", children: [
2349
+ "Showing the first ",
2350
+ formatSize(TEXT_LIMIT),
2351
+ "."
2352
+ ] }) : null
2353
+ ] });
2354
+ }
2355
+ if (kind === "image") {
2356
+ return /* @__PURE__ */ jsx29("img", { ref: imageRef, className: "sc-file-preview__image", alt: file.name });
2357
+ }
2358
+ return /* @__PURE__ */ jsxs24("p", { className: "sc-file-preview__note", children: [
2359
+ "There is no preview for ",
2360
+ fileTypeLabel(file),
2361
+ " files."
2362
+ ] });
2363
+ }
2195
2364
 
2196
2365
  // src/components/FileInput/FileInput.tsx
2197
- import { jsx as jsx29, jsxs as jsxs24 } from "react/jsx-runtime";
2366
+ import { jsx as jsx30, jsxs as jsxs25 } from "react/jsx-runtime";
2198
2367
  var REMOVE_ICON = '<svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><path d="M4 4l8 8M12 4l-8 8"/></svg>';
2199
2368
  var sameFiles = (a, b) => a.length === b.length && a.every((file, i) => file === b[i]);
2200
2369
  function FileInput({
@@ -2217,25 +2386,29 @@ function FileInput({
2217
2386
  defaultFiles,
2218
2387
  onFilesChange,
2219
2388
  idleLabel,
2220
- browseLabel = "browse"
2389
+ browseLabel = "browse",
2390
+ preview = true
2221
2391
  }) {
2222
2392
  const id = useId3();
2223
2393
  const labelId = `${id}label`;
2224
2394
  const descriptionId = `${id}description`;
2225
2395
  const messagesId = `${id}messages`;
2226
2396
  const controlled = files !== void 0;
2227
- const [uncontrolledFiles, setUncontrolledFiles] = useState5(defaultFiles ?? []);
2397
+ const [uncontrolledFiles, setUncontrolledFiles] = useState6(defaultFiles ?? []);
2228
2398
  const current = controlled ? files : uncontrolledFiles;
2229
- const [rejections, setRejections] = useState5([]);
2230
- const [inputId, setInputId] = useState5();
2231
- const [pondVersion, setPondVersion] = useState5(0);
2232
- const hostRef = useRef3(null);
2233
- const pondRef = useRef3(null);
2234
- const moduleRef = useRef3(null);
2235
- const patchRef = useRef3(() => {
2399
+ const [rejections, setRejections] = useState6([]);
2400
+ const [inputId, setInputId] = useState6();
2401
+ const [previewFile, setPreviewFile] = useState6(null);
2402
+ const [previewOpen, setPreviewOpen] = useState6(false);
2403
+ const previewTriggerRef = useRef4(null);
2404
+ const [pondVersion, setPondVersion] = useState6(0);
2405
+ const hostRef = useRef4(null);
2406
+ const pondRef = useRef4(null);
2407
+ const moduleRef = useRef4(null);
2408
+ const patchRef = useRef4(() => {
2236
2409
  });
2237
- const settledRef = useRef3(current);
2238
- const targetRef = useRef3(null);
2410
+ const settledRef = useRef4(current);
2411
+ const targetRef = useRef4(null);
2239
2412
  const acceptList = parseAccept(accept);
2240
2413
  const acceptAttr = acceptList.join(",");
2241
2414
  const maxBytes = parseSize(maxFileSize);
@@ -2254,13 +2427,19 @@ function FileInput({
2254
2427
  labelId,
2255
2428
  describedBy: [description != null ? descriptionId : null, messages.length > 0 ? messagesId : null],
2256
2429
  isInvalid,
2257
- acceptAttr
2430
+ acceptAttr,
2431
+ preview: preview && !disabled,
2432
+ openPreview: (file, trigger) => {
2433
+ previewTriggerRef.current = trigger;
2434
+ setPreviewFile(file);
2435
+ setPreviewOpen(true);
2436
+ }
2258
2437
  };
2259
- const liveRef = useRef3(live);
2260
- useEffect3(() => {
2438
+ const liveRef = useRef4(live);
2439
+ useEffect4(() => {
2261
2440
  liveRef.current = live;
2262
2441
  });
2263
- useEffect3(() => {
2442
+ useEffect4(() => {
2264
2443
  let cancelled = false;
2265
2444
  let pond = null;
2266
2445
  let observer = null;
@@ -2287,6 +2466,15 @@ function FileInput({
2287
2466
  const noteRemove = (event) => {
2288
2467
  if (event.target?.closest?.(".filepond--action-remove-item")) removeIntent = true;
2289
2468
  };
2469
+ const onPreviewClick = (event) => {
2470
+ const button = event.target?.closest?.(
2471
+ ".sc-file-input__preview"
2472
+ );
2473
+ const file = button?.dataset.fileId ? pond?.getFile(button.dataset.fileId)?.file : null;
2474
+ if (!button || !(file instanceof File)) return;
2475
+ event.stopPropagation();
2476
+ liveRef.current.openPreview(file, button);
2477
+ };
2290
2478
  const emit = () => {
2291
2479
  const mod = moduleRef.current;
2292
2480
  if (!pond || !mod) return;
@@ -2328,11 +2516,33 @@ function FileInput({
2328
2516
  set(root.querySelector(".filepond--label-action"), "tabindex", null);
2329
2517
  for (const item of root.querySelectorAll(".filepond--item")) {
2330
2518
  const fileName = item.querySelector(".filepond--file-info-main")?.textContent?.trim();
2331
- if (fileName)
2332
- set(item.querySelector(".filepond--action-remove-item"), "aria-label", `Remove ${fileName}`);
2519
+ const remove = item.querySelector(".filepond--action-remove-item");
2520
+ if (fileName) set(remove, "aria-label", `Remove ${fileName}`);
2521
+ patchPreview(item, remove);
2333
2522
  }
2334
2523
  setInputId(input?.id || void 0);
2335
2524
  };
2525
+ const patchPreview = (item, remove) => {
2526
+ const row = item.querySelector(".filepond--file");
2527
+ if (!row) return;
2528
+ let button = row.querySelector(":scope > .sc-file-input__preview");
2529
+ const id2 = item.id.replace(/^filepond--item-/, "");
2530
+ const file = pond?.getFile(id2)?.file;
2531
+ if (!liveRef.current.preview || !(file instanceof File) || previewKind(file) == null) {
2532
+ button?.remove();
2533
+ return;
2534
+ }
2535
+ if (!button) {
2536
+ button = document.createElement("button");
2537
+ button.type = "button";
2538
+ button.className = "sc-file-input__preview";
2539
+ row.insertBefore(button, remove ?? row.firstChild);
2540
+ }
2541
+ if (button.dataset.fileId !== id2) button.dataset.fileId = id2;
2542
+ if (button.textContent !== file.name) button.textContent = file.name;
2543
+ const name2 = `Preview ${file.name}`;
2544
+ if (button.getAttribute("aria-label") !== name2) button.setAttribute("aria-label", name2);
2545
+ };
2336
2546
  void import("filepond").then((mod) => {
2337
2547
  if (cancelled || !mod.supported()) return;
2338
2548
  moduleRef.current = mod;
@@ -2397,6 +2607,7 @@ function FileInput({
2397
2607
  host.addEventListener("drop", startAttempt, true);
2398
2608
  host.addEventListener("change", startAttempt, true);
2399
2609
  host.addEventListener("click", noteRemove, true);
2610
+ host.addEventListener("click", onPreviewClick);
2400
2611
  settledRef.current = [];
2401
2612
  setPondVersion((v) => v + 1);
2402
2613
  });
@@ -2405,6 +2616,7 @@ function FileInput({
2405
2616
  host.removeEventListener("drop", startAttempt, true);
2406
2617
  host.removeEventListener("change", startAttempt, true);
2407
2618
  host.removeEventListener("click", noteRemove, true);
2619
+ host.removeEventListener("click", onPreviewClick);
2408
2620
  observer?.disconnect();
2409
2621
  if (pond) {
2410
2622
  pond.element?.remove();
@@ -2415,7 +2627,7 @@ function FileInput({
2415
2627
  };
2416
2628
  };
2417
2629
  }, []);
2418
- useEffect3(() => {
2630
+ useEffect4(() => {
2419
2631
  const pond = pondRef.current;
2420
2632
  if (!pond) return;
2421
2633
  pond.setOptions({
@@ -2441,9 +2653,10 @@ function FileInput({
2441
2653
  acceptAttr,
2442
2654
  isInvalid,
2443
2655
  description,
2444
- messages.length
2656
+ messages.length,
2657
+ preview
2445
2658
  ]);
2446
- useEffect3(() => {
2659
+ useEffect4(() => {
2447
2660
  const pond = pondRef.current;
2448
2661
  const mod = moduleRef.current;
2449
2662
  if (!pond || !mod) return;
@@ -2460,7 +2673,7 @@ function FileInput({
2460
2673
  if (toAdd.length > 0) void pond.addFiles(toAdd);
2461
2674
  }, [current, pondVersion]);
2462
2675
  const ready = pondVersion > 0;
2463
- return /* @__PURE__ */ jsxs24(
2676
+ return /* @__PURE__ */ jsxs25(
2464
2677
  "div",
2465
2678
  {
2466
2679
  className: cx("sc-field", "sc-file-input-field", className),
@@ -2468,7 +2681,7 @@ function FileInput({
2468
2681
  "data-disabled": disabled ? "" : void 0,
2469
2682
  "data-invalid": isInvalid ? "" : void 0,
2470
2683
  children: [
2471
- /* @__PURE__ */ jsxs24(
2684
+ /* @__PURE__ */ jsxs25(
2472
2685
  "label",
2473
2686
  {
2474
2687
  id: labelId,
@@ -2476,11 +2689,11 @@ function FileInput({
2476
2689
  className: cx("sc-field__label", hideLabel && "sc-visually-hidden"),
2477
2690
  children: [
2478
2691
  label,
2479
- required ? /* @__PURE__ */ jsx29("span", { className: "sc-field__required", "aria-hidden": "true", children: "*" }) : null
2692
+ required ? /* @__PURE__ */ jsx30("span", { className: "sc-field__required", "aria-hidden": "true", children: "*" }) : null
2480
2693
  ]
2481
2694
  }
2482
2695
  ),
2483
- /* @__PURE__ */ jsxs24(
2696
+ /* @__PURE__ */ jsxs25(
2484
2697
  "div",
2485
2698
  {
2486
2699
  className: "sc-file-input",
@@ -2491,18 +2704,31 @@ function FileInput({
2491
2704
  children: [
2492
2705
  ready ? null : (
2493
2706
  // Server render and first paint: the same drop zone, inert until FilePond takes over.
2494
- /* @__PURE__ */ jsxs24("div", { className: "sc-file-input__placeholder", "aria-hidden": "true", children: [
2707
+ /* @__PURE__ */ jsxs25("div", { className: "sc-file-input__placeholder", "aria-hidden": "true", children: [
2495
2708
  idleText,
2496
2709
  " ",
2497
- /* @__PURE__ */ jsx29("span", { className: "sc-file-input__browse", children: browseLabel })
2710
+ /* @__PURE__ */ jsx30("span", { className: "sc-file-input__browse", children: browseLabel })
2498
2711
  ] })
2499
2712
  ),
2500
- /* @__PURE__ */ jsx29("div", { ref: hostRef, className: "sc-file-input__pond" })
2713
+ /* @__PURE__ */ jsx30("div", { ref: hostRef, className: "sc-file-input__pond" })
2501
2714
  ]
2502
2715
  }
2503
2716
  ),
2504
- description != null ? /* @__PURE__ */ jsx29("p", { id: descriptionId, className: "sc-field__description", children: description }) : null,
2505
- /* @__PURE__ */ jsx29("div", { id: messagesId, className: "sc-file-input__messages", "aria-live": "polite", children: messages.map((message, i) => /* @__PURE__ */ jsx29("p", { className: "sc-field__error", children: message }, i)) })
2717
+ description != null ? /* @__PURE__ */ jsx30("p", { id: descriptionId, className: "sc-field__description", children: description }) : null,
2718
+ /* @__PURE__ */ jsx30("div", { id: messagesId, className: "sc-file-input__messages", "aria-live": "polite", children: messages.map((message, i) => /* @__PURE__ */ jsx30("p", { className: "sc-field__error", children: message }, i)) }),
2719
+ previewFile ? /* @__PURE__ */ jsx30(
2720
+ FilePreviewDialog,
2721
+ {
2722
+ file: previewFile,
2723
+ open: previewOpen,
2724
+ onOpenChange: setPreviewOpen,
2725
+ finalFocus: () => {
2726
+ const trigger = previewTriggerRef.current;
2727
+ if (trigger?.isConnected) return trigger;
2728
+ return hostRef.current?.querySelector("input.filepond--browser") ?? true;
2729
+ }
2730
+ }
2731
+ ) : null
2506
2732
  ]
2507
2733
  }
2508
2734
  );
@@ -2510,11 +2736,11 @@ function FileInput({
2510
2736
 
2511
2737
  // src/components/PriorityNav/PriorityNav.tsx
2512
2738
  import {
2513
- useEffect as useEffect4,
2739
+ useEffect as useEffect5,
2514
2740
  useId as useId4,
2515
2741
  useLayoutEffect as useLayoutEffect2,
2516
- useRef as useRef4,
2517
- useState as useState6
2742
+ useRef as useRef5,
2743
+ useState as useState7
2518
2744
  } from "react";
2519
2745
 
2520
2746
  // src/components/PriorityNav/fit.ts
@@ -2570,9 +2796,9 @@ function placePanel({ anchor, panel, viewport, gutter, offset, rtl = false }) {
2570
2796
  }
2571
2797
 
2572
2798
  // src/components/PriorityNav/PriorityNav.tsx
2573
- import { jsx as jsx30, jsxs as jsxs25 } from "react/jsx-runtime";
2799
+ import { jsx as jsx31, jsxs as jsxs26 } from "react/jsx-runtime";
2574
2800
  var defaultCountLabel2 = (n) => n === 1 ? "1 link" : `${n} links`;
2575
- var defaultLink = ({ children, ...props }) => /* @__PURE__ */ jsx30("a", { ...props, children });
2801
+ var defaultLink = ({ children, ...props }) => /* @__PURE__ */ jsx31("a", { ...props, children });
2576
2802
  var keyOf = (item) => item.id ?? item.href;
2577
2803
  var supportsAnchor = () => typeof CSS !== "undefined" && typeof CSS.supports === "function" && CSS.supports("anchor-name: --a");
2578
2804
  var supportsPopover = (el) => typeof el.showPopover === "function";
@@ -2595,18 +2821,18 @@ function PriorityNav({
2595
2821
  }) {
2596
2822
  const signature = signatureOf(items);
2597
2823
  const current = items.findIndex((item) => item.current);
2598
- const [fit, setFit] = useState6({ signature: null, current: -1, count: 0, overflowing: false });
2599
- const [open, setOpen] = useState6(false);
2824
+ const [fit, setFit] = useState7({ signature: null, current: -1, count: 0, overflowing: false });
2825
+ const [open, setOpen] = useState7(false);
2600
2826
  const panelId = useId4();
2601
- const listRef = useRef4(null);
2602
- const moreRef = useRef4(null);
2603
- const toggleRef = useRef4(null);
2604
- const panelRef = useRef4(null);
2605
- const measured = useRef4(null);
2606
- const currentRef = useRef4(current);
2607
- const fitRef = useRef4(fit);
2608
- const hysteresisRef = useRef4(overflowHysteresis);
2609
- const onOverflowRef = useRef4(onOverflowChange);
2827
+ const listRef = useRef5(null);
2828
+ const moreRef = useRef5(null);
2829
+ const toggleRef = useRef5(null);
2830
+ const panelRef = useRef5(null);
2831
+ const measured = useRef5(null);
2832
+ const currentRef = useRef5(current);
2833
+ const fitRef = useRef5(fit);
2834
+ const hysteresisRef = useRef5(overflowHysteresis);
2835
+ const onOverflowRef = useRef5(onOverflowChange);
2610
2836
  const solve = (m, cur, was) => ({
2611
2837
  count: fitCount({ ...m, current: cur }),
2612
2838
  overflowing: isOverflowing({ ...m, was, hysteresis: hysteresisRef.current })
@@ -2638,7 +2864,7 @@ function PriorityNav({
2638
2864
  if (!m) return;
2639
2865
  setFit({ signature, current, ...solve(m, current, fit.overflowing) });
2640
2866
  }, [measuring, signature, current, fit]);
2641
- useEffect4(() => {
2867
+ useEffect5(() => {
2642
2868
  const list = listRef.current;
2643
2869
  const more = moreRef.current;
2644
2870
  if (!list || !more || typeof ResizeObserver === "undefined") return;
@@ -2662,13 +2888,13 @@ function PriorityNav({
2662
2888
  observer.observe(more);
2663
2889
  return () => observer.disconnect();
2664
2890
  }, []);
2665
- const reported = useRef4(false);
2666
- useEffect4(() => {
2891
+ const reported = useRef5(false);
2892
+ useEffect5(() => {
2667
2893
  if (reported.current === fit.overflowing) return;
2668
2894
  reported.current = fit.overflowing;
2669
2895
  onOverflowRef.current?.(fit.overflowing);
2670
2896
  }, [fit.overflowing]);
2671
- useEffect4(() => {
2897
+ useEffect5(() => {
2672
2898
  const fonts = typeof document === "undefined" ? void 0 : document.fonts;
2673
2899
  if (!fonts || fonts.status === "loaded") return;
2674
2900
  let live = true;
@@ -2719,7 +2945,7 @@ function PriorityNav({
2719
2945
  panel.style.top = "";
2720
2946
  };
2721
2947
  }, [expanded]);
2722
- useEffect4(() => {
2948
+ useEffect5(() => {
2723
2949
  if (!expanded) return;
2724
2950
  const onPointerDown = (event) => {
2725
2951
  if (!moreRef.current?.contains(event.target)) setOpen(false);
@@ -2747,11 +2973,11 @@ function PriorityNav({
2747
2973
  className: "sc-priority-nav__link",
2748
2974
  "aria-current": item.current ? "page" : void 0,
2749
2975
  onClick: inPanel ? () => setOpen(false) : void 0,
2750
- children: /* @__PURE__ */ jsx30("span", { className: "sc-priority-nav__label", children: item.label })
2976
+ children: /* @__PURE__ */ jsx31("span", { className: "sc-priority-nav__label", children: item.label })
2751
2977
  },
2752
2978
  item
2753
2979
  );
2754
- return /* @__PURE__ */ jsx30(
2980
+ return /* @__PURE__ */ jsx31(
2755
2981
  "nav",
2756
2982
  {
2757
2983
  className: cx("sc-priority-nav", className),
@@ -2759,11 +2985,11 @@ function PriorityNav({
2759
2985
  "data-overflowing": fit.overflowing || void 0,
2760
2986
  style: { ...style, "--_anchor": `--sc-priority-nav${panelId.replace(/[^\w-]/g, "")}` },
2761
2987
  ...rest,
2762
- children: /* @__PURE__ */ jsxs25("ul", { ref: listRef, className: "sc-priority-nav__list", children: [
2988
+ children: /* @__PURE__ */ jsxs26("ul", { ref: listRef, className: "sc-priority-nav__list", children: [
2763
2989
  items.map(
2764
- (item, i) => flags && !flags[i] ? null : /* @__PURE__ */ jsx30("li", { className: "sc-priority-nav__item", "data-current": item.current || void 0, children: link(item, false) }, keyOf(item))
2990
+ (item, i) => flags && !flags[i] ? null : /* @__PURE__ */ jsx31("li", { className: "sc-priority-nav__item", "data-current": item.current || void 0, children: link(item, false) }, keyOf(item))
2765
2991
  ),
2766
- /* @__PURE__ */ jsxs25(
2992
+ /* @__PURE__ */ jsxs26(
2767
2993
  "li",
2768
2994
  {
2769
2995
  ref: moreRef,
@@ -2771,7 +2997,7 @@ function PriorityNav({
2771
2997
  hidden: measuring || overflow.length === 0,
2772
2998
  onBlur,
2773
2999
  children: [
2774
- /* @__PURE__ */ jsxs25(
3000
+ /* @__PURE__ */ jsxs26(
2775
3001
  "button",
2776
3002
  {
2777
3003
  ref: toggleRef,
@@ -2781,17 +3007,17 @@ function PriorityNav({
2781
3007
  "aria-controls": panelId,
2782
3008
  onClick: () => setOpen((o) => !o),
2783
3009
  children: [
2784
- /* @__PURE__ */ jsx30(MoreHorizontalIcon, { className: "sc-priority-nav__ellipsis" }),
2785
- /* @__PURE__ */ jsx30("span", { className: "sc-priority-nav__toggle-label", children: moreLabel }),
2786
- /* @__PURE__ */ jsxs25("span", { className: "sc-visually-hidden", children: [
3010
+ /* @__PURE__ */ jsx31(MoreHorizontalIcon, { className: "sc-priority-nav__ellipsis" }),
3011
+ /* @__PURE__ */ jsx31("span", { className: "sc-priority-nav__toggle-label", children: moreLabel }),
3012
+ /* @__PURE__ */ jsxs26("span", { className: "sc-visually-hidden", children: [
2787
3013
  ", ",
2788
3014
  moreCountLabel(overflow.length)
2789
3015
  ] }),
2790
- /* @__PURE__ */ jsx30(ChevronDownIcon, { size: "sm", className: "sc-priority-nav__chevron" })
3016
+ /* @__PURE__ */ jsx31(ChevronDownIcon, { size: "sm", className: "sc-priority-nav__chevron" })
2791
3017
  ]
2792
3018
  }
2793
3019
  ),
2794
- /* @__PURE__ */ jsx30("ul", { ref: panelRef, id: panelId, className: "sc-priority-nav__panel", hidden: !expanded, children: overflow.map((item) => /* @__PURE__ */ jsx30("li", { children: link(item, true) }, keyOf(item))) })
3020
+ /* @__PURE__ */ jsx31("ul", { ref: panelRef, id: panelId, className: "sc-priority-nav__panel", hidden: !expanded, children: overflow.map((item) => /* @__PURE__ */ jsx31("li", { children: link(item, true) }, keyOf(item))) })
2795
3021
  ]
2796
3022
  }
2797
3023
  )
@@ -2802,9 +3028,9 @@ function PriorityNav({
2802
3028
 
2803
3029
  // src/components/Popover/Popover.tsx
2804
3030
  import { Popover as BasePopover } from "@base-ui/react/popover";
2805
- import { jsx as jsx31, jsxs as jsxs26 } from "react/jsx-runtime";
3031
+ import { jsx as jsx32, jsxs as jsxs27 } from "react/jsx-runtime";
2806
3032
  function Root6({ open, defaultOpen, onOpenChange, modal = false, children }) {
2807
- return /* @__PURE__ */ jsx31(
3033
+ return /* @__PURE__ */ jsx32(
2808
3034
  BasePopover.Root,
2809
3035
  {
2810
3036
  open,
@@ -2816,10 +3042,10 @@ function Root6({ open, defaultOpen, onOpenChange, modal = false, children }) {
2816
3042
  );
2817
3043
  }
2818
3044
  function Trigger3({ render, className, children, ...rest }) {
2819
- return /* @__PURE__ */ jsx31(
3045
+ return /* @__PURE__ */ jsx32(
2820
3046
  BasePopover.Trigger,
2821
3047
  {
2822
- render: render ?? /* @__PURE__ */ jsx31(Button, { variant: "secondary", className }),
3048
+ render: render ?? /* @__PURE__ */ jsx32(Button, { variant: "secondary", className }),
2823
3049
  className: render ? className : void 0,
2824
3050
  ...rest,
2825
3051
  children
@@ -2835,38 +3061,38 @@ function Content3({
2835
3061
  children,
2836
3062
  ...rest
2837
3063
  }) {
2838
- return /* @__PURE__ */ jsx31(BasePopover.Portal, { children: /* @__PURE__ */ jsx31(
3064
+ return /* @__PURE__ */ jsx32(BasePopover.Portal, { children: /* @__PURE__ */ jsx32(
2839
3065
  BasePopover.Positioner,
2840
3066
  {
2841
3067
  side,
2842
3068
  align,
2843
3069
  sideOffset: sideOffset ?? (arrow ? 8 : 4),
2844
3070
  className: "sc-popover__positioner",
2845
- children: /* @__PURE__ */ jsxs26(BasePopover.Popup, { className: cx("sc-popover", className), ...rest, children: [
2846
- arrow ? /* @__PURE__ */ jsx31(BasePopover.Arrow, { className: "sc-popover__arrow" }) : null,
3071
+ children: /* @__PURE__ */ jsxs27(BasePopover.Popup, { className: cx("sc-popover", className), ...rest, children: [
3072
+ arrow ? /* @__PURE__ */ jsx32(BasePopover.Arrow, { className: "sc-popover__arrow" }) : null,
2847
3073
  children
2848
3074
  ] })
2849
3075
  }
2850
3076
  ) });
2851
3077
  }
2852
3078
  function Title2({ className, ...rest }) {
2853
- return /* @__PURE__ */ jsx31(BasePopover.Title, { className: cx("sc-popover__title", className), ...rest });
3079
+ return /* @__PURE__ */ jsx32(BasePopover.Title, { className: cx("sc-popover__title", className), ...rest });
2854
3080
  }
2855
3081
  function Description2({ className, ...rest }) {
2856
- return /* @__PURE__ */ jsx31(BasePopover.Description, { className: cx("sc-popover__description", className), ...rest });
3082
+ return /* @__PURE__ */ jsx32(BasePopover.Description, { className: cx("sc-popover__description", className), ...rest });
2857
3083
  }
2858
3084
  function Close2({ className, children, render, ...rest }) {
2859
3085
  if (children === void 0 && render === void 0) {
2860
- return /* @__PURE__ */ jsx31(BasePopover.Close, { className: cx("sc-popover__close", className), "aria-label": "Close", ...rest, children: /* @__PURE__ */ jsx31(XIcon, {}) });
3086
+ return /* @__PURE__ */ jsx32(BasePopover.Close, { className: cx("sc-popover__close", className), "aria-label": "Close", ...rest, children: /* @__PURE__ */ jsx32(XIcon, {}) });
2861
3087
  }
2862
- return /* @__PURE__ */ jsx31(BasePopover.Close, { className, render, ...rest, children });
3088
+ return /* @__PURE__ */ jsx32(BasePopover.Close, { className, render, ...rest, children });
2863
3089
  }
2864
3090
  var Popover = { Root: Root6, Trigger: Trigger3, Content: Content3, Title: Title2, Description: Description2, Close: Close2 };
2865
3091
 
2866
3092
  // src/components/BroomIcon/BroomIcon.tsx
2867
- import { jsx as jsx32, jsxs as jsxs27 } from "react/jsx-runtime";
3093
+ import { jsx as jsx33, jsxs as jsxs28 } from "react/jsx-runtime";
2868
3094
  function BroomIcon({ sweeping = false, size = "lg", title, className, ...rest }) {
2869
- return /* @__PURE__ */ jsxs27(
3095
+ return /* @__PURE__ */ jsxs28(
2870
3096
  "svg",
2871
3097
  {
2872
3098
  className: cx("sc-icon", "sc-broom", className),
@@ -2880,7 +3106,7 @@ function BroomIcon({ sweeping = false, size = "lg", title, className, ...rest })
2880
3106
  "aria-label": title,
2881
3107
  ...rest,
2882
3108
  children: [
2883
- /* @__PURE__ */ jsxs27(
3109
+ /* @__PURE__ */ jsxs28(
2884
3110
  "g",
2885
3111
  {
2886
3112
  className: "sc-broom__body",
@@ -2889,18 +3115,18 @@ function BroomIcon({ sweeping = false, size = "lg", title, className, ...rest })
2889
3115
  strokeLinecap: "round",
2890
3116
  strokeLinejoin: "round",
2891
3117
  children: [
2892
- /* @__PURE__ */ jsx32("path", { d: "M16.1 10.48 L19.36 1.86 A0.87 0.87 0 0 1 20.98 2.47 L17.84 10.78" }),
2893
- /* @__PURE__ */ jsx32("path", { d: "M13.33 12 C14.23 10.67 16.73 9.87 18.17 11 C19.07 12.67 18.93 18.33 18.4 21.4 C16.07 22.4 11.07 22.07 6.4 18.07 C9.07 16.67 11.8 15 13.33 12 Z" }),
2894
- /* @__PURE__ */ jsx32("path", { className: "sc-broom__band", d: "M13.47 12.13 L18.4 13.5 L18.33 14.1 L13.27 12.77 Z" }),
2895
- /* @__PURE__ */ jsx32("path", { d: "M12.87 13.73 L14.4 14.47 M16.2 15.27 L18.47 15.87" }),
2896
- /* @__PURE__ */ jsx32("path", { d: "M12.8 16.87 C11.86 18.24 10.75 19.38 9.57 20.23 M15.6 17.4 C15.22 18.96 14.6 20.44 13.77 21.71" })
3118
+ /* @__PURE__ */ jsx33("path", { d: "M16.1 10.48 L19.36 1.86 A0.87 0.87 0 0 1 20.98 2.47 L17.84 10.78" }),
3119
+ /* @__PURE__ */ jsx33("path", { d: "M13.33 12 C14.23 10.67 16.73 9.87 18.17 11 C19.07 12.67 18.93 18.33 18.4 21.4 C16.07 22.4 11.07 22.07 6.4 18.07 C9.07 16.67 11.8 15 13.33 12 Z" }),
3120
+ /* @__PURE__ */ jsx33("path", { className: "sc-broom__band", d: "M13.47 12.13 L18.4 13.5 L18.33 14.1 L13.27 12.77 Z" }),
3121
+ /* @__PURE__ */ jsx33("path", { d: "M12.87 13.73 L14.4 14.47 M16.2 15.27 L18.47 15.87" }),
3122
+ /* @__PURE__ */ jsx33("path", { d: "M12.8 16.87 C11.86 18.24 10.75 19.38 9.57 20.23 M15.6 17.4 C15.22 18.96 14.6 20.44 13.77 21.71" })
2897
3123
  ]
2898
3124
  }
2899
3125
  ),
2900
- /* @__PURE__ */ jsxs27("g", { className: "sc-broom__dust", fill: "none", stroke: "currentColor", children: [
2901
- /* @__PURE__ */ jsx32("circle", { cx: "4.77", cy: "13.53", r: "0.5" }),
2902
- /* @__PURE__ */ jsx32("circle", { cx: "2.7", cy: "15.53", r: "0.87" }),
2903
- /* @__PURE__ */ jsx32("circle", { cx: "4.1", cy: "18.27", r: "0.5" })
3126
+ /* @__PURE__ */ jsxs28("g", { className: "sc-broom__dust", fill: "none", stroke: "currentColor", children: [
3127
+ /* @__PURE__ */ jsx33("circle", { cx: "4.77", cy: "13.53", r: "0.5" }),
3128
+ /* @__PURE__ */ jsx33("circle", { cx: "2.7", cy: "15.53", r: "0.87" }),
3129
+ /* @__PURE__ */ jsx33("circle", { cx: "4.1", cy: "18.27", r: "0.5" })
2904
3130
  ] })
2905
3131
  ]
2906
3132
  }
@@ -2943,6 +3169,7 @@ export {
2943
3169
  ExternalLinkIcon,
2944
3170
  EyeIcon,
2945
3171
  FileInput,
3172
+ FilePreviewDialog,
2946
3173
  FilterIcon,
2947
3174
  FlagIcon,
2948
3175
  Heading,