@geomak/ui 7.7.4 → 7.8.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/index.js CHANGED
@@ -2,7 +2,7 @@ import { X, Error as Error$1, Warning, CheckCircle, Info } from './chunk-KAFJJO5
2
2
  export { icons_default as Icon, createIcon } from './chunk-KAFJJO5O.js';
3
3
  import { colors_default } from './chunk-DXOWXLKK.js';
4
4
  export { colors_default as COLORS, PALETTE as palette, semanticTokens, vars } from './chunk-DXOWXLKK.js';
5
- import React31, { useMemo, createContext, useState, useEffect, useId, useCallback, useRef, useContext, useSyncExternalStore, useLayoutEffect } from 'react';
5
+ import React34, { useMemo, createContext, useState, useEffect, useId, useCallback, useRef, useContext, useSyncExternalStore, useLayoutEffect } from 'react';
6
6
  import { createPortal } from 'react-dom';
7
7
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
8
8
  import * as AvatarPrimitive from '@radix-ui/react-avatar';
@@ -453,7 +453,7 @@ function Typography({
453
453
  }
454
454
  );
455
455
  }
456
- var IconButton = React31.forwardRef(function IconButton2({
456
+ var IconButton = React34.forwardRef(function IconButton2({
457
457
  icon,
458
458
  onClick,
459
459
  type = "primary",
@@ -562,7 +562,7 @@ var SIZE_CLASSES = {
562
562
  md: "h-9 px-4 text-sm gap-1.5 rounded-lg",
563
563
  lg: "h-11 px-5 text-sm gap-2 rounded-xl"
564
564
  };
565
- var Button = React31.forwardRef(function Button2({
565
+ var Button = React34.forwardRef(function Button2({
566
566
  content,
567
567
  variant = "primary",
568
568
  size = "md",
@@ -670,7 +670,7 @@ function MenuButton({
670
670
  "data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95",
671
671
  "data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95"
672
672
  ].join(" "),
673
- children: items.map((item) => /* @__PURE__ */ jsxs(React31.Fragment, { children: [
673
+ children: items.map((item) => /* @__PURE__ */ jsxs(React34.Fragment, { children: [
674
674
  item.separatorBefore && /* @__PURE__ */ jsx(DropdownMenu2.Separator, { className: "my-1 h-px bg-border" }),
675
675
  /* @__PURE__ */ jsxs(
676
676
  DropdownMenu2.Item,
@@ -1817,7 +1817,7 @@ function Kbd({
1817
1817
  style
1818
1818
  }) {
1819
1819
  if (keys && keys.length > 0) {
1820
- return /* @__PURE__ */ jsx("span", { className: cx("inline-flex items-center gap-1", className), style, children: keys.map((k, i) => /* @__PURE__ */ jsxs(React31.Fragment, { children: [
1820
+ return /* @__PURE__ */ jsx("span", { className: cx("inline-flex items-center gap-1", className), style, children: keys.map((k, i) => /* @__PURE__ */ jsxs(React34.Fragment, { children: [
1821
1821
  i > 0 && /* @__PURE__ */ jsx("span", { className: "text-foreground-muted text-xs select-none", children: separator }),
1822
1822
  /* @__PURE__ */ jsx("kbd", { className: [cap, SIZE3[size]].join(" "), children: k })
1823
1823
  ] }, `${k}-${i}`)) });
@@ -1909,7 +1909,7 @@ function FlatCarousel({
1909
1909
  style
1910
1910
  }) {
1911
1911
  const scrollerRef = useRef(null);
1912
- const slides = React31.Children.toArray(children);
1912
+ const slides = React34.Children.toArray(children);
1913
1913
  const [active, setActive] = useState(0);
1914
1914
  const [atStart, setAtStart] = useState(true);
1915
1915
  const [atEnd, setAtEnd] = useState(false);
@@ -1964,7 +1964,7 @@ function RotatingCarousel({
1964
1964
  className = "",
1965
1965
  style
1966
1966
  }) {
1967
- const slides = React31.Children.toArray(children);
1967
+ const slides = React34.Children.toArray(children);
1968
1968
  const count = slides.length;
1969
1969
  const [active, setActive] = useState(0);
1970
1970
  const reduced = useReducedMotion();
@@ -5245,7 +5245,7 @@ function Wizard({
5245
5245
  ] });
5246
5246
  }
5247
5247
  var SearchIcon = /* @__PURE__ */ jsx("svg", { xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 24 24", fill: "currentColor", className: "w-4 h-4", "aria-hidden": "true", children: /* @__PURE__ */ jsx("path", { fillRule: "evenodd", d: "M10.5 3.75a6.75 6.75 0 100 13.5 6.75 6.75 0 000-13.5zM2.25 10.5a8.25 8.25 0 1114.59 5.28l4.69 4.69a.75.75 0 11-1.06 1.06l-4.69-4.69A8.25 8.25 0 012.25 10.5z", clipRule: "evenodd" }) });
5248
- var SearchInput = React31.forwardRef(function SearchInput2({ value, onChange, disabled, label, htmlFor, placeholder, name, inputStyle, style, layout = "vertical", size = "md", icon, helperText, className }, ref) {
5248
+ var SearchInput = React34.forwardRef(function SearchInput2({ value, onChange, disabled, label, htmlFor, placeholder, name, inputStyle, style, layout = "vertical", size = "md", icon, helperText, className }, ref) {
5249
5249
  return /* @__PURE__ */ jsx(Field, { className, label, htmlFor, layout, helperText, children: /* @__PURE__ */ jsxs(
5250
5250
  "div",
5251
5251
  {
@@ -5562,7 +5562,7 @@ function TableBody({
5562
5562
  return /* @__PURE__ */ jsx("tbody", { children: rows.map((row, i) => {
5563
5563
  const rowKey = getRowKey(row, i);
5564
5564
  const isExpanded = expanded.has(rowKey);
5565
- return /* @__PURE__ */ jsxs(React31.Fragment, { children: [
5565
+ return /* @__PURE__ */ jsxs(React34.Fragment, { children: [
5566
5566
  /* @__PURE__ */ jsxs(
5567
5567
  "tr",
5568
5568
  {
@@ -5913,6 +5913,769 @@ function VirtualList({
5913
5913
  )
5914
5914
  ] });
5915
5915
  }
5916
+
5917
+ // src/utils/fileSource.ts
5918
+ function isUrlSource(source) {
5919
+ return typeof source === "string" || source instanceof URL;
5920
+ }
5921
+ function urlHref(source) {
5922
+ return typeof source === "string" ? source : source.href;
5923
+ }
5924
+ async function sourceToBytes(source, remote) {
5925
+ if (source instanceof Uint8Array) return source;
5926
+ if (source instanceof ArrayBuffer) return new Uint8Array(source);
5927
+ if (typeof Blob !== "undefined" && source instanceof Blob) {
5928
+ return new Uint8Array(await source.arrayBuffer());
5929
+ }
5930
+ if (isUrlSource(source)) {
5931
+ const res = await fetch(urlHref(source), {
5932
+ headers: remote?.httpHeaders,
5933
+ credentials: remote?.withCredentials ? "include" : "same-origin"
5934
+ });
5935
+ if (!res.ok) throw new Error(`Failed to fetch (${res.status} ${res.statusText})`);
5936
+ return new Uint8Array(await res.arrayBuffer());
5937
+ }
5938
+ throw new Error("Unsupported source type");
5939
+ }
5940
+ function sourceName(source) {
5941
+ if (typeof File !== "undefined" && source instanceof File) return source.name;
5942
+ if (isUrlSource(source)) {
5943
+ const href = urlHref(source);
5944
+ const last = href.split("?")[0].split("#")[0].split("/").filter(Boolean).pop();
5945
+ return last || void 0;
5946
+ }
5947
+ return void 0;
5948
+ }
5949
+ var pdfjsPromise = null;
5950
+ function loadPdfjs() {
5951
+ if (pdfjsPromise) return pdfjsPromise;
5952
+ pdfjsPromise = import('pdfjs-dist').then((pdfjs) => {
5953
+ if (!pdfjs.GlobalWorkerOptions.workerSrc) {
5954
+ pdfjs.GlobalWorkerOptions.workerSrc = `https://cdn.jsdelivr.net/npm/pdfjs-dist@${pdfjs.version}/build/pdf.worker.min.mjs`;
5955
+ }
5956
+ return pdfjs;
5957
+ });
5958
+ return pdfjsPromise;
5959
+ }
5960
+ var GAP = 12;
5961
+ function PdfViewer({
5962
+ source,
5963
+ remote,
5964
+ initialPage = 1,
5965
+ zoom = "page-width",
5966
+ toolbar = true,
5967
+ thumbnails = false,
5968
+ textLayer = true,
5969
+ onLoad,
5970
+ onError,
5971
+ onPageChange,
5972
+ className = "",
5973
+ style
5974
+ }) {
5975
+ const [pdfjs, setPdfjs] = useState(null);
5976
+ const [doc, setDoc] = useState(null);
5977
+ const [numPages, setNumPages] = useState(0);
5978
+ const [baseSize, setBaseSize] = useState(null);
5979
+ const [status, setStatus] = useState("loading");
5980
+ const [error, setError] = useState(null);
5981
+ const [reloadKey, setReloadKey] = useState(0);
5982
+ const scrollRef = useRef(null);
5983
+ const [scrollTop, setScrollTop] = useState(0);
5984
+ const [viewport, setViewport] = useState({ w: 0, h: 0 });
5985
+ const [zoomMode, setZoomMode] = useState(zoom);
5986
+ const [page, setPage] = useState(initialPage);
5987
+ const [showSearch, setShowSearch] = useState(false);
5988
+ const [query, setQuery] = useState("");
5989
+ const [matchPages, setMatchPages] = useState(null);
5990
+ const [matchIdx, setMatchIdx] = useState(0);
5991
+ const tb = toolbar === true ? { zoom: true, pager: true, download: true, print: true, search: true } : toolbar || {};
5992
+ useEffect(() => {
5993
+ let cancelled = false;
5994
+ let task;
5995
+ setStatus("loading");
5996
+ setError(null);
5997
+ setDoc(null);
5998
+ setBaseSize(null);
5999
+ loadPdfjs().then(async (pdfjs2) => {
6000
+ if (cancelled) return;
6001
+ setPdfjs(pdfjs2);
6002
+ const params = isUrlSource(source) ? { url: urlHref(source), httpHeaders: remote?.httpHeaders, withCredentials: remote?.withCredentials } : { data: source instanceof Uint8Array || source instanceof ArrayBuffer ? source : new Uint8Array(await source.arrayBuffer()) };
6003
+ task = pdfjs2.getDocument(params);
6004
+ const pdf = await task.promise;
6005
+ if (cancelled) {
6006
+ pdf.destroy?.();
6007
+ return;
6008
+ }
6009
+ const first = await pdf.getPage(1);
6010
+ const vp = first.getViewport({ scale: 1 });
6011
+ if (cancelled) return;
6012
+ setDoc(pdf);
6013
+ setNumPages(pdf.numPages);
6014
+ setBaseSize({ width: vp.width, height: vp.height });
6015
+ setStatus("ready");
6016
+ onLoad?.({ numPages: pdf.numPages });
6017
+ }).catch((err) => {
6018
+ if (cancelled) return;
6019
+ setStatus("error");
6020
+ setError(err);
6021
+ onError?.(err);
6022
+ });
6023
+ return () => {
6024
+ cancelled = true;
6025
+ task?.destroy?.();
6026
+ };
6027
+ }, [source, remote, reloadKey]);
6028
+ useEffect(() => () => {
6029
+ doc?.destroy?.();
6030
+ }, [doc]);
6031
+ useEffect(() => {
6032
+ const el = scrollRef.current;
6033
+ if (!el || typeof ResizeObserver === "undefined") return;
6034
+ const measure = () => setViewport({ w: el.clientWidth, h: el.clientHeight });
6035
+ measure();
6036
+ const ro = new ResizeObserver(measure);
6037
+ ro.observe(el);
6038
+ return () => ro.disconnect();
6039
+ }, [status]);
6040
+ const scale = useMemo(() => {
6041
+ if (!baseSize) return 1;
6042
+ if (typeof zoomMode === "number") return zoomMode;
6043
+ const avail = Math.max(0, (viewport.w || baseSize.width) - 32);
6044
+ if (zoomMode === "page-width" || zoomMode === "auto") return avail / baseSize.width;
6045
+ if (zoomMode === "page-fit") {
6046
+ const availH = Math.max(0, (viewport.h || baseSize.height) - 32);
6047
+ return Math.min(avail / baseSize.width, availH / baseSize.height);
6048
+ }
6049
+ return 1;
6050
+ }, [zoomMode, baseSize, viewport]);
6051
+ const pageH = baseSize ? baseSize.height * scale + GAP : 0;
6052
+ const pageW = baseSize ? baseSize.width * scale : 0;
6053
+ const total = numPages * pageH;
6054
+ const overscan = 1;
6055
+ const startIdx = pageH ? Math.max(0, Math.floor(scrollTop / pageH) - overscan) : 0;
6056
+ const endIdx = pageH ? Math.min(numPages, Math.ceil((scrollTop + viewport.h) / pageH) + overscan) : 0;
6057
+ const visiblePages = Array.from({ length: Math.max(0, endIdx - startIdx) }, (_, i) => startIdx + i + 1);
6058
+ useEffect(() => {
6059
+ if (!pageH) return;
6060
+ const cur = Math.min(numPages, Math.max(1, Math.floor((scrollTop + viewport.h / 2) / pageH) + 1));
6061
+ if (cur !== page) {
6062
+ setPage(cur);
6063
+ onPageChange?.(cur);
6064
+ }
6065
+ }, [scrollTop, pageH, viewport.h, numPages]);
6066
+ const scrollToPage = useCallback((p) => {
6067
+ const el = scrollRef.current;
6068
+ if (!el || !pageH) return;
6069
+ el.scrollTo({ top: (p - 1) * pageH, behavior: "smooth" });
6070
+ }, [pageH]);
6071
+ useEffect(() => {
6072
+ if (status === "ready" && initialPage > 1) scrollToPage(initialPage);
6073
+ }, [status]);
6074
+ const runSearch = useCallback(async (q) => {
6075
+ setQuery(q);
6076
+ if (!doc || !q.trim()) {
6077
+ setMatchPages(null);
6078
+ setMatchIdx(0);
6079
+ return;
6080
+ }
6081
+ const needle = q.toLowerCase();
6082
+ const hits = [];
6083
+ for (let p = 1; p <= numPages; p++) {
6084
+ const pg = await doc.getPage(p);
6085
+ const tc = await pg.getTextContent();
6086
+ const text = tc.items.map((it) => "str" in it ? it.str : "").join(" ").toLowerCase();
6087
+ if (text.includes(needle)) hits.push(p);
6088
+ }
6089
+ setMatchPages(hits);
6090
+ setMatchIdx(0);
6091
+ if (hits.length) scrollToPage(hits[0]);
6092
+ }, [doc, numPages, scrollToPage]);
6093
+ const gotoMatch = (dir) => {
6094
+ if (!matchPages?.length) return;
6095
+ const next = (matchIdx + dir + matchPages.length) % matchPages.length;
6096
+ setMatchIdx(next);
6097
+ scrollToPage(matchPages[next]);
6098
+ };
6099
+ const getBytes = useCallback(async () => {
6100
+ if (doc?.getData) return doc.getData();
6101
+ if (source instanceof Uint8Array) return source;
6102
+ if (source instanceof ArrayBuffer) return new Uint8Array(source);
6103
+ if (typeof Blob !== "undefined" && source instanceof Blob) return new Uint8Array(await source.arrayBuffer());
6104
+ throw new Error("No bytes available");
6105
+ }, [doc, source]);
6106
+ const download2 = useCallback(async () => {
6107
+ const bytes = await getBytes();
6108
+ const blob = new Blob([bytes], { type: "application/pdf" });
6109
+ const url = URL.createObjectURL(blob);
6110
+ const a = document.createElement("a");
6111
+ a.href = url;
6112
+ a.download = sourceName(source) || "document.pdf";
6113
+ a.click();
6114
+ setTimeout(() => URL.revokeObjectURL(url), 1e3);
6115
+ }, [getBytes, source]);
6116
+ const print = useCallback(async () => {
6117
+ const bytes = await getBytes();
6118
+ const blob = new Blob([bytes], { type: "application/pdf" });
6119
+ const url = URL.createObjectURL(blob);
6120
+ const iframe = document.createElement("iframe");
6121
+ iframe.style.cssText = "position:fixed;right:0;bottom:0;width:0;height:0;border:0";
6122
+ iframe.src = url;
6123
+ iframe.onload = () => {
6124
+ try {
6125
+ iframe.contentWindow?.focus();
6126
+ iframe.contentWindow?.print();
6127
+ } catch {
6128
+ }
6129
+ };
6130
+ document.body.appendChild(iframe);
6131
+ setTimeout(() => {
6132
+ document.body.removeChild(iframe);
6133
+ URL.revokeObjectURL(url);
6134
+ }, 6e4);
6135
+ }, [getBytes]);
6136
+ const setZoomNum = (factor) => {
6137
+ const cur = typeof zoomMode === "number" ? zoomMode : scale;
6138
+ setZoomMode(Math.min(5, Math.max(0.25, +(cur * factor).toFixed(2))));
6139
+ };
6140
+ if (status === "error") {
6141
+ return /* @__PURE__ */ jsxs("div", { className: cx("flex flex-col items-center justify-center gap-3 rounded-lg border border-border bg-surface p-8 text-center", className), style, children: [
6142
+ /* @__PURE__ */ jsx("p", { className: "text-sm font-medium text-status-error", children: "Couldn\u2019t load the PDF" }),
6143
+ error?.message && /* @__PURE__ */ jsx("p", { className: "max-w-md text-xs text-foreground-muted", children: error.message }),
6144
+ /* @__PURE__ */ jsx(Button_default, { content: "Retry", size: "sm", variant: "outline", onClick: () => setReloadKey((k) => k + 1) })
6145
+ ] });
6146
+ }
6147
+ return /* @__PURE__ */ jsxs("div", { className: cx("flex flex-col overflow-hidden rounded-lg border border-border bg-surface-raised", className), style: { height: 600, ...style }, children: [
6148
+ toolbar !== false && /* @__PURE__ */ jsxs("div", { className: "flex flex-shrink-0 flex-wrap items-center gap-2 border-b border-border bg-surface px-2 py-1.5", children: [
6149
+ tb.pager && /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1", children: [
6150
+ /* @__PURE__ */ jsx(IconButton_default, { type: "ghost", size: "sm", title: "Previous page", onClick: () => scrollToPage(Math.max(1, page - 1)), icon: /* @__PURE__ */ jsx(Chevron5, { dir: "up" }), disabled: status !== "ready" }),
6151
+ /* @__PURE__ */ jsxs("span", { className: "px-1 text-xs tabular-nums text-foreground-secondary select-none", children: [
6152
+ status === "ready" ? page : "\u2013",
6153
+ " / ",
6154
+ numPages || "\u2013"
6155
+ ] }),
6156
+ /* @__PURE__ */ jsx(IconButton_default, { type: "ghost", size: "sm", title: "Next page", onClick: () => scrollToPage(Math.min(numPages, page + 1)), icon: /* @__PURE__ */ jsx(Chevron5, { dir: "down" }), disabled: status !== "ready" })
6157
+ ] }),
6158
+ tb.zoom && /* @__PURE__ */ jsxs("div", { className: "ml-1 flex items-center gap-1", children: [
6159
+ /* @__PURE__ */ jsx(IconButton_default, { type: "ghost", size: "sm", title: "Zoom out", onClick: () => setZoomNum(1 / 1.2), icon: /* @__PURE__ */ jsx("span", { className: "text-base leading-none", children: "\u2212" }), disabled: status !== "ready" }),
6160
+ /* @__PURE__ */ jsx(IconButton_default, { type: "ghost", size: "sm", title: "Zoom in", onClick: () => setZoomNum(1.2), icon: /* @__PURE__ */ jsx("span", { className: "text-base leading-none", children: "+" }), disabled: status !== "ready" }),
6161
+ /* @__PURE__ */ jsx(IconButton_default, { type: "ghost", size: "sm", title: "Fit width", onClick: () => setZoomMode("page-width"), icon: /* @__PURE__ */ jsx(FitWidthIcon, {}), disabled: status !== "ready" }),
6162
+ /* @__PURE__ */ jsx(IconButton_default, { type: "ghost", size: "sm", title: "Fit page", onClick: () => setZoomMode("page-fit"), icon: /* @__PURE__ */ jsx(FitPageIcon, {}), disabled: status !== "ready" })
6163
+ ] }),
6164
+ /* @__PURE__ */ jsxs("div", { className: "ml-auto flex items-center gap-1", children: [
6165
+ tb.search && /* @__PURE__ */ jsx(IconButton_default, { type: "ghost", size: "sm", title: "Search", onClick: () => setShowSearch((s) => !s), icon: /* @__PURE__ */ jsx(SearchIcon2, {}), disabled: status !== "ready" }),
6166
+ tb.download && /* @__PURE__ */ jsx(IconButton_default, { type: "ghost", size: "sm", title: "Download", onClick: download2, icon: /* @__PURE__ */ jsx(DownloadIcon, {}), disabled: status !== "ready" }),
6167
+ tb.print && /* @__PURE__ */ jsx(IconButton_default, { type: "ghost", size: "sm", title: "Print", onClick: print, icon: /* @__PURE__ */ jsx(PrintIcon, {}), disabled: status !== "ready" })
6168
+ ] }),
6169
+ tb.search && showSearch && /* @__PURE__ */ jsxs("div", { className: "flex w-full items-center gap-2 pt-1", children: [
6170
+ /* @__PURE__ */ jsx(
6171
+ "input",
6172
+ {
6173
+ autoFocus: true,
6174
+ value: query,
6175
+ onChange: (e) => runSearch(e.target.value),
6176
+ placeholder: "Find in document\u2026",
6177
+ className: "h-7 flex-1 rounded-md border border-border bg-surface px-2 text-sm text-foreground outline-none focus:border-accent"
6178
+ }
6179
+ ),
6180
+ /* @__PURE__ */ jsx("span", { className: "text-xs tabular-nums text-foreground-muted", children: matchPages == null ? "" : matchPages.length ? `${matchIdx + 1}/${matchPages.length} pages` : "no matches" }),
6181
+ /* @__PURE__ */ jsx(IconButton_default, { type: "ghost", size: "sm", title: "Previous match", onClick: () => gotoMatch(-1), icon: /* @__PURE__ */ jsx(Chevron5, { dir: "up" }), disabled: !matchPages?.length }),
6182
+ /* @__PURE__ */ jsx(IconButton_default, { type: "ghost", size: "sm", title: "Next match", onClick: () => gotoMatch(1), icon: /* @__PURE__ */ jsx(Chevron5, { dir: "down" }), disabled: !matchPages?.length })
6183
+ ] })
6184
+ ] }),
6185
+ /* @__PURE__ */ jsxs("div", { className: "flex min-h-0 flex-1", children: [
6186
+ thumbnails && status === "ready" && doc && baseSize && /* @__PURE__ */ jsx("div", { className: "w-32 flex-shrink-0 overflow-y-auto border-r border-border bg-surface p-2", children: Array.from({ length: numPages }, (_, i) => i + 1).map((p) => /* @__PURE__ */ jsx(
6187
+ "button",
6188
+ {
6189
+ type: "button",
6190
+ onClick: () => scrollToPage(p),
6191
+ className: cx(
6192
+ "mb-2 block w-full overflow-hidden rounded border bg-surface-raised transition-colors",
6193
+ p === page ? "border-accent ring-1 ring-accent" : "border-border hover:border-border-strong"
6194
+ ),
6195
+ style: { aspectRatio: `${baseSize.width} / ${baseSize.height}` },
6196
+ "aria-label": `Page ${p}`,
6197
+ children: Math.abs(p - page) <= 8 ? /* @__PURE__ */ jsx(PdfPage, { pdfjs, doc, page: p, scale: 112 / baseSize.width, textLayer: false }) : /* @__PURE__ */ jsx("span", { className: "block py-4 text-center text-xs text-foreground-muted", children: p })
6198
+ },
6199
+ p
6200
+ )) }),
6201
+ /* @__PURE__ */ jsx(
6202
+ "div",
6203
+ {
6204
+ ref: scrollRef,
6205
+ onScroll: (e) => setScrollTop(e.currentTarget.scrollTop),
6206
+ className: "relative flex-1 overflow-auto bg-background",
6207
+ children: status === "loading" || !baseSize ? /* @__PURE__ */ jsx("div", { className: "flex flex-col items-center gap-3 p-6", children: /* @__PURE__ */ jsx(SkeletonBox, { width: Math.min(viewport.w - 48, 560) || 480, height: 680, className: "rounded" }) }) : numPages === 0 ? /* @__PURE__ */ jsx("div", { className: "flex h-full items-center justify-center text-sm text-foreground-muted", children: "Empty document" }) : /* @__PURE__ */ jsx("div", { style: { height: total, position: "relative" }, children: visiblePages.map((p) => /* @__PURE__ */ jsx(
6208
+ "div",
6209
+ {
6210
+ style: { position: "absolute", top: (p - 1) * pageH, left: 0, right: 0, height: pageH, display: "flex", justifyContent: "center", paddingTop: GAP / 2, paddingBottom: GAP / 2 },
6211
+ children: /* @__PURE__ */ jsx("div", { className: "relative shadow-md", style: { width: pageW, height: baseSize.height * scale }, children: /* @__PURE__ */ jsx(PdfPage, { pdfjs, doc, page: p, scale, textLayer }) })
6212
+ },
6213
+ p
6214
+ )) })
6215
+ }
6216
+ )
6217
+ ] })
6218
+ ] });
6219
+ }
6220
+ function PdfPage({ pdfjs, doc, page, scale, textLayer }) {
6221
+ const canvasRef = useRef(null);
6222
+ const textRef = useRef(null);
6223
+ useEffect(() => {
6224
+ let cancelled = false;
6225
+ let renderTask;
6226
+ (async () => {
6227
+ const pg = await doc.getPage(page);
6228
+ if (cancelled) return;
6229
+ const viewport = pg.getViewport({ scale });
6230
+ const canvas = canvasRef.current;
6231
+ if (!canvas) return;
6232
+ const ratio = typeof window !== "undefined" ? window.devicePixelRatio || 1 : 1;
6233
+ canvas.width = Math.floor(viewport.width * ratio);
6234
+ canvas.height = Math.floor(viewport.height * ratio);
6235
+ canvas.style.width = `${Math.floor(viewport.width)}px`;
6236
+ canvas.style.height = `${Math.floor(viewport.height)}px`;
6237
+ const ctx = canvas.getContext("2d");
6238
+ renderTask = pg.render({ canvasContext: ctx, viewport, transform: ratio !== 1 ? [ratio, 0, 0, ratio, 0, 0] : void 0 });
6239
+ try {
6240
+ await renderTask.promise;
6241
+ } catch {
6242
+ return;
6243
+ }
6244
+ if (cancelled || !textLayer || !textRef.current || !pdfjs?.TextLayer) return;
6245
+ try {
6246
+ textRef.current.innerHTML = "";
6247
+ textRef.current.style.setProperty("--scale-factor", String(scale));
6248
+ const tl = new pdfjs.TextLayer({ textContentSource: pg.streamTextContent(), container: textRef.current, viewport });
6249
+ await tl.render();
6250
+ } catch {
6251
+ }
6252
+ })();
6253
+ return () => {
6254
+ cancelled = true;
6255
+ renderTask?.cancel?.();
6256
+ };
6257
+ }, [pdfjs, doc, page, scale, textLayer]);
6258
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
6259
+ /* @__PURE__ */ jsx("canvas", { ref: canvasRef, className: "block bg-white" }),
6260
+ textLayer && /* @__PURE__ */ jsx("div", { ref: textRef, className: "textLayer pointer-events-auto absolute inset-0 overflow-hidden leading-none", style: { opacity: 0.25 }, "aria-hidden": "true" })
6261
+ ] });
6262
+ }
6263
+ var Chevron5 = ({ dir }) => /* @__PURE__ */ jsx("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: 2, className: "h-4 w-4", "aria-hidden": "true", children: /* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: dir === "up" ? "m6 15 6-6 6 6" : "m6 9 6 6 6-6" }) });
6264
+ var SearchIcon2 = () => /* @__PURE__ */ jsxs("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: 2, className: "h-4 w-4", "aria-hidden": "true", children: [
6265
+ /* @__PURE__ */ jsx("circle", { cx: "11", cy: "11", r: "7" }),
6266
+ /* @__PURE__ */ jsx("path", { strokeLinecap: "round", d: "m21 21-4.3-4.3" })
6267
+ ] });
6268
+ var DownloadIcon = () => /* @__PURE__ */ jsx("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: 2, className: "h-4 w-4", "aria-hidden": "true", children: /* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M12 3v12m0 0 4-4m-4 4-4-4M4 21h16" }) });
6269
+ var PrintIcon = () => /* @__PURE__ */ jsx("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: 2, className: "h-4 w-4", "aria-hidden": "true", children: /* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M6 9V3h12v6M6 18H4v-7h16v7h-2M8 14h8v7H8z" }) });
6270
+ var FitWidthIcon = () => /* @__PURE__ */ jsx("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: 2, className: "h-4 w-4", "aria-hidden": "true", children: /* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M3 12h18m0 0-4-4m4 4-4 4M3 12l4-4m-4 4 4 4" }) });
6271
+ var FitPageIcon = () => /* @__PURE__ */ jsxs("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: 2, className: "h-4 w-4", "aria-hidden": "true", children: [
6272
+ /* @__PURE__ */ jsx("rect", { x: "5", y: "3", width: "14", height: "18", rx: "1" }),
6273
+ /* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M9 8h6M9 12h6M9 16h4" })
6274
+ ] });
6275
+ var DEFAULT_COL_WIDTH = 140;
6276
+ var GUTTER = 52;
6277
+ function resolveWidth(w) {
6278
+ if (typeof w === "number") return w;
6279
+ if (typeof w === "string") {
6280
+ const n = parseFloat(w);
6281
+ if (!Number.isNaN(n) && /^\d/.test(w.trim())) return n;
6282
+ }
6283
+ return DEFAULT_COL_WIDTH;
6284
+ }
6285
+ function displayValue(v) {
6286
+ if (v == null) return "";
6287
+ return String(v);
6288
+ }
6289
+ function DataGrid({
6290
+ columns,
6291
+ rows,
6292
+ rowHeight = 34,
6293
+ headerHeight = 38,
6294
+ height = 480,
6295
+ editable = false,
6296
+ virtualize = true,
6297
+ overscan = 4,
6298
+ rowNumbers = true,
6299
+ onCellEdit,
6300
+ className = "",
6301
+ style,
6302
+ emptyState = "No data"
6303
+ }) {
6304
+ const scrollRef = useRef(null);
6305
+ const [scroll, setScroll] = useState({ top: 0, left: 0 });
6306
+ const [viewport, setViewport] = useState({ w: 0, h: 0 });
6307
+ const [editing, setEditing] = useState(null);
6308
+ const [draft, setDraft] = useState("");
6309
+ const gutter = rowNumbers ? GUTTER : 0;
6310
+ const { widths, offsets, totalWidth } = useMemo(() => {
6311
+ const widths2 = columns.map((c) => resolveWidth(c.width));
6312
+ const offsets2 = [];
6313
+ let acc = 0;
6314
+ for (const w of widths2) {
6315
+ offsets2.push(acc);
6316
+ acc += w;
6317
+ }
6318
+ return { widths: widths2, offsets: offsets2, totalWidth: acc };
6319
+ }, [columns]);
6320
+ const totalHeight = rows.length * rowHeight;
6321
+ useEffect(() => {
6322
+ const el = scrollRef.current;
6323
+ if (!el || typeof ResizeObserver === "undefined") return;
6324
+ const measure = () => setViewport({ w: el.clientWidth, h: el.clientHeight });
6325
+ measure();
6326
+ const ro = new ResizeObserver(measure);
6327
+ ro.observe(el);
6328
+ return () => ro.disconnect();
6329
+ }, []);
6330
+ const bodyH = (viewport.h || (typeof height === "number" ? height : 480)) - headerHeight;
6331
+ const rowStart = virtualize ? Math.max(0, Math.floor(scroll.top / rowHeight) - overscan) : 0;
6332
+ const rowEnd = virtualize ? Math.min(rows.length, Math.ceil((scroll.top + bodyH) / rowHeight) + overscan) : rows.length;
6333
+ let colStart = 0;
6334
+ let colEnd = columns.length;
6335
+ if (virtualize && viewport.w) {
6336
+ const viewLeft = scroll.left;
6337
+ const viewRight = scroll.left + (viewport.w - gutter);
6338
+ colStart = Math.max(0, offsets.findIndex((o, i) => o + widths[i] > viewLeft));
6339
+ if (colStart < 0) colStart = 0;
6340
+ colEnd = offsets.findIndex((o) => o > viewRight);
6341
+ colEnd = colEnd === -1 ? columns.length : Math.min(columns.length, colEnd + 1);
6342
+ colStart = Math.max(0, colStart - overscan);
6343
+ colEnd = Math.min(columns.length, colEnd + overscan);
6344
+ }
6345
+ const visibleRows = Array.from({ length: Math.max(0, rowEnd - rowStart) }, (_, i) => rowStart + i);
6346
+ const visibleCols = Array.from({ length: Math.max(0, colEnd - colStart) }, (_, i) => colStart + i);
6347
+ const commit = useCallback(() => {
6348
+ if (!editing) return;
6349
+ const col = columns[editing.col];
6350
+ onCellEdit?.({ row: editing.row, column: col.key, value: draft });
6351
+ setEditing(null);
6352
+ }, [editing, columns, draft, onCellEdit]);
6353
+ const startEdit = (row, col) => {
6354
+ const c = columns[col];
6355
+ if (!(c.editable ?? editable)) return;
6356
+ setDraft(displayValue(rows[row]?.[c.key] ?? ""));
6357
+ setEditing({ row, col });
6358
+ };
6359
+ return /* @__PURE__ */ jsxs(
6360
+ "div",
6361
+ {
6362
+ ref: scrollRef,
6363
+ onScroll: (e) => setScroll({ top: e.currentTarget.scrollTop, left: e.currentTarget.scrollLeft }),
6364
+ className: cx("relative overflow-auto rounded-lg border border-border bg-surface-raised text-sm", className),
6365
+ style: { height, ...style },
6366
+ role: "grid",
6367
+ "aria-rowcount": rows.length,
6368
+ "aria-colcount": columns.length,
6369
+ children: [
6370
+ /* @__PURE__ */ jsxs("div", { style: { position: "relative", width: gutter + totalWidth, height: headerHeight + totalHeight }, children: [
6371
+ rowNumbers && /* @__PURE__ */ jsx(
6372
+ "div",
6373
+ {
6374
+ className: "border-b border-r border-border bg-surface",
6375
+ style: { position: "absolute", top: scroll.top, left: scroll.left, width: gutter, height: headerHeight, zIndex: 3 }
6376
+ }
6377
+ ),
6378
+ visibleCols.map((ci) => {
6379
+ const c = columns[ci];
6380
+ return /* @__PURE__ */ jsx(
6381
+ "div",
6382
+ {
6383
+ role: "columnheader",
6384
+ className: "flex items-center border-b border-r border-border bg-surface px-3 font-medium text-foreground-secondary",
6385
+ style: {
6386
+ position: "absolute",
6387
+ top: scroll.top,
6388
+ left: gutter + offsets[ci],
6389
+ width: widths[ci],
6390
+ height: headerHeight,
6391
+ zIndex: 2,
6392
+ justifyContent: c.align === "right" ? "flex-end" : c.align === "center" ? "center" : "flex-start"
6393
+ },
6394
+ children: /* @__PURE__ */ jsx("span", { className: "truncate", children: c.label ?? c.key })
6395
+ },
6396
+ `h-${c.key}`
6397
+ );
6398
+ }),
6399
+ rowNumbers && visibleRows.map((ri) => /* @__PURE__ */ jsx(
6400
+ "div",
6401
+ {
6402
+ className: "flex items-center justify-center border-b border-r border-border bg-surface text-xs tabular-nums text-foreground-muted",
6403
+ style: { position: "absolute", left: scroll.left, top: headerHeight + ri * rowHeight, width: gutter, height: rowHeight, zIndex: 1 },
6404
+ children: ri + 1
6405
+ },
6406
+ `g-${ri}`
6407
+ )),
6408
+ visibleRows.map(
6409
+ (ri) => visibleCols.map((ci) => {
6410
+ const c = columns[ci];
6411
+ const isEditing = editing?.row === ri && editing?.col === ci;
6412
+ const canEdit = c.editable ?? editable;
6413
+ return /* @__PURE__ */ jsx(
6414
+ "div",
6415
+ {
6416
+ role: "gridcell",
6417
+ onDoubleClick: () => startEdit(ri, ci),
6418
+ className: cx(
6419
+ "flex items-center border-b border-r border-border px-3",
6420
+ ri % 2 ? "bg-surface-raised" : "bg-surface",
6421
+ canEdit && "cursor-text"
6422
+ ),
6423
+ style: {
6424
+ position: "absolute",
6425
+ top: headerHeight + ri * rowHeight,
6426
+ left: gutter + offsets[ci],
6427
+ width: widths[ci],
6428
+ height: rowHeight,
6429
+ justifyContent: c.align === "right" ? "flex-end" : c.align === "center" ? "center" : "flex-start"
6430
+ },
6431
+ children: isEditing ? /* @__PURE__ */ jsx(
6432
+ "input",
6433
+ {
6434
+ autoFocus: true,
6435
+ value: draft,
6436
+ onChange: (e) => setDraft(e.target.value),
6437
+ onBlur: commit,
6438
+ onKeyDown: (e) => {
6439
+ if (e.key === "Enter") commit();
6440
+ else if (e.key === "Escape") setEditing(null);
6441
+ },
6442
+ className: "h-full w-full bg-transparent text-foreground outline-none"
6443
+ }
6444
+ ) : /* @__PURE__ */ jsx("span", { className: "truncate text-foreground", children: displayValue(rows[ri]?.[c.key] ?? "") })
6445
+ },
6446
+ `${ri}-${c.key}`
6447
+ );
6448
+ })
6449
+ )
6450
+ ] }),
6451
+ rows.length === 0 && /* @__PURE__ */ jsx("div", { className: "absolute inset-0 flex items-center justify-center text-foreground-muted", style: { top: headerHeight }, children: emptyState })
6452
+ ]
6453
+ }
6454
+ );
6455
+ }
6456
+ function toColumns(cols) {
6457
+ return cols.map((c) => typeof c === "string" ? { key: c, label: c } : { ...c, label: c.label ?? c.key });
6458
+ }
6459
+ function cellValue(v) {
6460
+ if (v != null && typeof v === "object" && "value" in v) return v.value;
6461
+ return v;
6462
+ }
6463
+ function toPlainRows(sheet, columns) {
6464
+ return sheet.rows.map((row) => {
6465
+ const out = {};
6466
+ for (const c of columns) out[c.key] = cellValue(row[c.key]);
6467
+ return out;
6468
+ });
6469
+ }
6470
+ var xlsxPromise = null;
6471
+ var loadXlsx = () => xlsxPromise ??= import('xlsx');
6472
+ var jspdfPromise = null;
6473
+ var loadJspdf = () => jspdfPromise ??= import('jspdf');
6474
+ function download(blob, name) {
6475
+ const url = URL.createObjectURL(blob);
6476
+ const a = document.createElement("a");
6477
+ a.href = url;
6478
+ a.download = name;
6479
+ a.click();
6480
+ setTimeout(() => URL.revokeObjectURL(url), 1e3);
6481
+ }
6482
+ function Spreadsheet({
6483
+ source,
6484
+ remote,
6485
+ editable = false,
6486
+ onCellEdit,
6487
+ onChange,
6488
+ export: exportFormats = ["xlsx", "csv", "pdf"],
6489
+ fileName,
6490
+ virtualize = true,
6491
+ className = "",
6492
+ style
6493
+ }) {
6494
+ const [sheets, setSheets] = useState(Array.isArray(source) ? source : null);
6495
+ const [active, setActive] = useState(0);
6496
+ const [status, setStatus] = useState(Array.isArray(source) ? "ready" : "loading");
6497
+ const [error, setError] = useState(null);
6498
+ const [reloadKey, setReloadKey] = useState(0);
6499
+ useEffect(() => {
6500
+ if (Array.isArray(source)) {
6501
+ setSheets(source);
6502
+ setStatus("ready");
6503
+ return;
6504
+ }
6505
+ let cancelled = false;
6506
+ setStatus("loading");
6507
+ setError(null);
6508
+ (async () => {
6509
+ try {
6510
+ const bytes = await sourceToBytes(source, remote);
6511
+ const XLSX = await loadXlsx();
6512
+ const wb = XLSX.read(bytes, { type: "array" });
6513
+ const parsed = wb.SheetNames.map((name) => {
6514
+ const ws = wb.Sheets[name];
6515
+ const aoa = XLSX.utils.sheet_to_json(ws, { header: 1, blankrows: false, defval: null });
6516
+ const headerRow = aoa[0] ?? [];
6517
+ const columns2 = headerRow.map((h, i) => ({
6518
+ key: h != null && String(h).length ? String(h) : `col_${i}`,
6519
+ label: h != null && String(h).length ? String(h) : `Column ${i + 1}`
6520
+ }));
6521
+ const rows = aoa.slice(1).map((r) => {
6522
+ const rec = {};
6523
+ columns2.forEach((c, i) => {
6524
+ rec[c.key] = r[i] ?? null;
6525
+ });
6526
+ return rec;
6527
+ });
6528
+ return { name, columns: columns2, rows };
6529
+ });
6530
+ if (cancelled) return;
6531
+ setSheets(parsed);
6532
+ setActive(0);
6533
+ setStatus("ready");
6534
+ } catch (err) {
6535
+ if (cancelled) return;
6536
+ setError(err);
6537
+ setStatus("error");
6538
+ }
6539
+ })();
6540
+ return () => {
6541
+ cancelled = true;
6542
+ };
6543
+ }, [source, remote, reloadKey]);
6544
+ const sheet = sheets?.[active];
6545
+ const columns = useMemo(() => sheet ? toColumns(sheet.columns) : [], [sheet]);
6546
+ const plainRows = useMemo(() => sheet ? toPlainRows(sheet, columns) : [], [sheet, columns]);
6547
+ const handleCellEdit = useCallback(({ row, column, value }) => {
6548
+ setSheets((prev) => {
6549
+ if (!prev) return prev;
6550
+ const next = prev.map((s, i) => i === active ? { ...s, rows: s.rows.map((r) => ({ ...r })) } : s);
6551
+ const target = next[active];
6552
+ const existing = target.rows[row]?.[column];
6553
+ if (existing != null && typeof existing === "object" && "value" in existing) {
6554
+ target.rows[row][column] = { ...existing, value };
6555
+ } else {
6556
+ target.rows[row][column] = value;
6557
+ }
6558
+ onChange?.(next);
6559
+ return next;
6560
+ });
6561
+ onCellEdit?.({ sheet: sheets?.[active]?.name ?? "", row, column, value });
6562
+ }, [active, onCellEdit, onChange, sheets]);
6563
+ const baseName = useMemo(
6564
+ () => (fileName || (typeof source === "object" && "name" in source ? sourceName(source) : null) || "spreadsheet").replace(/\.[^.]+$/, ""),
6565
+ [fileName, source]
6566
+ );
6567
+ const sheetAoa = useCallback((s) => {
6568
+ const cols = toColumns(s.columns);
6569
+ const header = cols.map((c) => typeof c.label === "string" ? c.label : c.key);
6570
+ const body = s.rows.map((r) => cols.map((c) => cellValue(r[c.key])));
6571
+ return [header, ...body];
6572
+ }, []);
6573
+ const exportXlsx = useCallback(async () => {
6574
+ if (!sheets) return;
6575
+ const XLSX = await loadXlsx();
6576
+ const wb = XLSX.utils.book_new();
6577
+ sheets.forEach((s) => {
6578
+ const ws = XLSX.utils.aoa_to_sheet(sheetAoa(s));
6579
+ XLSX.utils.book_append_sheet(wb, ws, (s.name || "Sheet").slice(0, 31));
6580
+ });
6581
+ const out = XLSX.write(wb, { bookType: "xlsx", type: "array" });
6582
+ download(new Blob([out], { type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" }), `${baseName}.xlsx`);
6583
+ }, [sheets, sheetAoa, baseName]);
6584
+ const exportCsv = useCallback(() => {
6585
+ if (!sheet) return;
6586
+ const aoa = sheetAoa(sheet);
6587
+ const esc = (v) => {
6588
+ const s = v == null ? "" : String(v);
6589
+ return /[",\n]/.test(s) ? `"${s.replace(/"/g, '""')}"` : s;
6590
+ };
6591
+ const csv = aoa.map((row) => row.map(esc).join(",")).join("\r\n");
6592
+ download(new Blob([`\uFEFF${csv}`], { type: "text/csv;charset=utf-8" }), `${baseName}.csv`);
6593
+ }, [sheet, sheetAoa, baseName]);
6594
+ const exportPdf = useCallback(async () => {
6595
+ if (!sheet) return;
6596
+ const { jsPDF } = await loadJspdf();
6597
+ const doc = new jsPDF({ orientation: "landscape", unit: "pt", format: "a4" });
6598
+ const aoa = sheetAoa(sheet);
6599
+ const margin2 = 32;
6600
+ const pageW = doc.internal.pageSize.getWidth();
6601
+ const pageH = doc.internal.pageSize.getHeight();
6602
+ const cols = aoa[0]?.length || 1;
6603
+ const colW = (pageW - margin2 * 2) / cols;
6604
+ const rowH = 18;
6605
+ let y = margin2;
6606
+ doc.setFontSize(9);
6607
+ const drawRow = (row, header) => {
6608
+ if (y + rowH > pageH - margin2) {
6609
+ doc.addPage();
6610
+ y = margin2;
6611
+ }
6612
+ doc.setFont("helvetica", header ? "bold" : "normal");
6613
+ row.forEach((cell, i) => {
6614
+ const text = cell == null ? "" : String(cell);
6615
+ doc.text(text.length > 24 ? `${text.slice(0, 23)}\u2026` : text, margin2 + i * colW + 2, y + 12);
6616
+ });
6617
+ doc.setDrawColor(210);
6618
+ doc.line(margin2, y + rowH, pageW - margin2, y + rowH);
6619
+ y += rowH;
6620
+ };
6621
+ doc.setFont("helvetica", "bold");
6622
+ doc.setFontSize(12);
6623
+ doc.text(sheet.name || baseName, margin2, y);
6624
+ y += 22;
6625
+ doc.setFontSize(9);
6626
+ aoa.forEach((row, i) => drawRow(row, i === 0));
6627
+ doc.save(`${baseName}.pdf`);
6628
+ }, [sheet, sheetAoa, baseName]);
6629
+ const runExport = (fmt) => {
6630
+ if (fmt === "xlsx") void exportXlsx();
6631
+ else if (fmt === "csv") exportCsv();
6632
+ else void exportPdf();
6633
+ };
6634
+ if (status === "error") {
6635
+ return /* @__PURE__ */ jsxs("div", { className: cx("flex flex-col items-center justify-center gap-3 rounded-lg border border-border bg-surface p-8 text-center", className), style, children: [
6636
+ /* @__PURE__ */ jsx("p", { className: "text-sm font-medium text-status-error", children: "Couldn\u2019t load the spreadsheet" }),
6637
+ error?.message && /* @__PURE__ */ jsx("p", { className: "max-w-md text-xs text-foreground-muted", children: error.message }),
6638
+ /* @__PURE__ */ jsx(Button_default, { content: "Retry", size: "sm", variant: "outline", onClick: () => setReloadKey((k) => k + 1) })
6639
+ ] });
6640
+ }
6641
+ if (status === "loading" || !sheets) {
6642
+ return /* @__PURE__ */ jsxs("div", { className: cx("rounded-lg border border-border bg-surface-raised p-4", className), style, children: [
6643
+ /* @__PURE__ */ jsx(SkeletonBox, { height: 32, className: "mb-3 rounded" }),
6644
+ /* @__PURE__ */ jsx(SkeletonBox, { height: 360, className: "rounded" })
6645
+ ] });
6646
+ }
6647
+ const formats = exportFormats || [];
6648
+ return /* @__PURE__ */ jsxs("div", { className: cx("flex flex-col gap-2", className), style, children: [
6649
+ (sheets.length > 1 || formats.length > 0) && /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [
6650
+ sheets.length > 1 && /* @__PURE__ */ jsx("div", { role: "tablist", className: "flex flex-wrap items-center gap-1 rounded-lg border border-border bg-surface p-1", children: sheets.map((s, i) => /* @__PURE__ */ jsx(
6651
+ "button",
6652
+ {
6653
+ role: "tab",
6654
+ type: "button",
6655
+ "aria-selected": i === active,
6656
+ onClick: () => setActive(i),
6657
+ className: cx(
6658
+ "rounded-md px-3 py-1 text-sm transition-colors",
6659
+ i === active ? "bg-accent text-accent-fg" : "text-foreground-secondary hover:bg-surface-raised hover:text-foreground"
6660
+ ),
6661
+ children: s.name || `Sheet ${i + 1}`
6662
+ },
6663
+ `${s.name}-${i}`
6664
+ )) }),
6665
+ formats.length > 0 && /* @__PURE__ */ jsx("div", { className: "ml-auto flex items-center gap-1.5", children: formats.map((fmt) => /* @__PURE__ */ jsx(Button_default, { content: fmt.toUpperCase(), size: "sm", variant: "outline", onClick: () => runExport(fmt) }, fmt)) })
6666
+ ] }),
6667
+ /* @__PURE__ */ jsx(
6668
+ DataGrid,
6669
+ {
6670
+ columns,
6671
+ rows: plainRows,
6672
+ editable,
6673
+ virtualize,
6674
+ onCellEdit: handleCellEdit
6675
+ }
6676
+ )
6677
+ ] });
6678
+ }
5916
6679
  function ThemeSwitch({ checked, onChange, label = "Toggle dark mode", className = "" }) {
5917
6680
  const id = useId();
5918
6681
  return /* @__PURE__ */ jsx("label", { htmlFor: id, className: `flex items-center gap-2 cursor-pointer select-none ${className}`.trim(), children: /* @__PURE__ */ jsx(
@@ -6013,7 +6776,7 @@ function FlyoutItems({ items }) {
6013
6776
  }
6014
6777
  function CollapsedFlyout({ item, trigger }) {
6015
6778
  const [open, setOpen] = useState(false);
6016
- const closeTimer = React31.useRef(void 0);
6779
+ const closeTimer = React34.useRef(void 0);
6017
6780
  const openNow = () => {
6018
6781
  window.clearTimeout(closeTimer.current);
6019
6782
  setOpen(true);
@@ -6022,7 +6785,7 @@ function CollapsedFlyout({ item, trigger }) {
6022
6785
  window.clearTimeout(closeTimer.current);
6023
6786
  closeTimer.current = window.setTimeout(() => setOpen(false), 150);
6024
6787
  };
6025
- React31.useEffect(() => () => window.clearTimeout(closeTimer.current), []);
6788
+ React34.useEffect(() => () => window.clearTimeout(closeTimer.current), []);
6026
6789
  return /* @__PURE__ */ jsxs(DropdownMenu2.Root, { open, onOpenChange: setOpen, modal: false, children: [
6027
6790
  /* @__PURE__ */ jsx(
6028
6791
  DropdownMenu2.Trigger,
@@ -6314,8 +7077,8 @@ function MegaMenuLink({ href, icon, description, active, onClick, children, clas
6314
7077
  function MegaMenuFeatured({ children, className = "" }) {
6315
7078
  return /* @__PURE__ */ jsx("div", { className: cx("min-w-0 rounded-lg bg-surface-raised border border-border p-4 flex flex-col", className), children });
6316
7079
  }
6317
- var elementsOfType = (children, type) => React31.Children.toArray(children).filter(
6318
- (c) => React31.isValidElement(c) && c.type === type
7080
+ var elementsOfType = (children, type) => React34.Children.toArray(children).filter(
7081
+ (c) => React34.isValidElement(c) && c.type === type
6319
7082
  );
6320
7083
  var MOBILE_CHEVRON = /* @__PURE__ */ jsx(
6321
7084
  "svg",
@@ -6352,9 +7115,9 @@ function MobileLinkRow({ link, onNavigate }) {
6352
7115
  );
6353
7116
  }
6354
7117
  function MobilePanel({ panel, onNavigate }) {
6355
- const nodes = React31.Children.toArray(panel.props.children);
7118
+ const nodes = React34.Children.toArray(panel.props.children);
6356
7119
  return /* @__PURE__ */ jsx("div", { className: "flex flex-col gap-4 px-2 pb-3 pt-1", children: nodes.map((node, i) => {
6357
- if (!React31.isValidElement(node)) return null;
7120
+ if (!React34.isValidElement(node)) return null;
6358
7121
  const el = node;
6359
7122
  if (el.type === MegaMenuSection) {
6360
7123
  const { title, children } = el.props;
@@ -6763,7 +7526,7 @@ function ThemeProvider({
6763
7526
  className = "",
6764
7527
  style
6765
7528
  }) {
6766
- const id = React31.useId().replace(/:/g, "");
7529
+ const id = React34.useId().replace(/:/g, "");
6767
7530
  const scopeClass = `geo-th-${id}`;
6768
7531
  const divRef = useRef(null);
6769
7532
  useEffect(() => {
@@ -7488,7 +8251,7 @@ function MultiTagRow({
7488
8251
  const wrap = wrapRef.current;
7489
8252
  const measure = measureRef.current;
7490
8253
  if (!wrap || !measure) return;
7491
- const GAP = 6;
8254
+ const GAP2 = 6;
7492
8255
  const recompute = () => {
7493
8256
  const avail = wrap.clientWidth;
7494
8257
  const tagEls = Array.from(measure.querySelectorAll("[data-mt]"));
@@ -7502,7 +8265,7 @@ function MultiTagRow({
7502
8265
  let used = 0;
7503
8266
  let count = 0;
7504
8267
  for (let i = 0; i < widths.length; i++) {
7505
- const w = widths[i] + (i > 0 ? GAP : 0);
8268
+ const w = widths[i] + (i > 0 ? GAP2 : 0);
7506
8269
  if (used + w <= avail) {
7507
8270
  used += w;
7508
8271
  count++;
@@ -7511,8 +8274,8 @@ function MultiTagRow({
7511
8274
  if (count < widths.length) {
7512
8275
  while (count > 0) {
7513
8276
  let t = 0;
7514
- for (let i = 0; i < count; i++) t += widths[i] + (i > 0 ? GAP : 0);
7515
- t += GAP + moreW;
8277
+ for (let i = 0; i < count; i++) t += widths[i] + (i > 0 ? GAP2 : 0);
8278
+ t += GAP2 + moreW;
7516
8279
  if (t <= avail) break;
7517
8280
  count--;
7518
8281
  }
@@ -8472,7 +9235,7 @@ function DatePicker({
8472
9235
  setOpen(false);
8473
9236
  }
8474
9237
  };
8475
- const displayValue = value ? format(value) : "";
9238
+ const displayValue2 = value ? format(value) : "";
8476
9239
  return /* @__PURE__ */ jsxs("div", { className: `flex flex-col gap-1 ${className}`.trim(), children: [
8477
9240
  /* @__PURE__ */ jsxs("div", { className: `flex ${layout === "vertical" ? "flex-col gap-1.5" : "flex-row items-start gap-3"}`, children: [
8478
9241
  /* @__PURE__ */ jsx(
@@ -8499,7 +9262,7 @@ function DatePicker({
8499
9262
  "aria-expanded": open,
8500
9263
  className: `flex items-center justify-between cursor-pointer select-none ${!style?.width ? "min-w-[200px]" : ""} ${fieldShell({ size, hasError, disabled })}`,
8501
9264
  children: [
8502
- /* @__PURE__ */ jsx("span", { className: `text-sm truncate ${displayValue ? "" : "text-foreground-muted"}`, children: displayValue || placeholder }),
9265
+ /* @__PURE__ */ jsx("span", { className: `text-sm truncate ${displayValue2 ? "" : "text-foreground-muted"}`, children: displayValue2 || placeholder }),
8503
9266
  /* @__PURE__ */ jsx(CalendarIcon, {})
8504
9267
  ]
8505
9268
  }
@@ -9071,7 +9834,7 @@ function OtpInput({
9071
9834
  emit(valid.join(""));
9072
9835
  focusBox(valid.length);
9073
9836
  };
9074
- return /* @__PURE__ */ jsx(Field, { className, label, htmlFor, errorId, errorMessage, required, layout, helperText, children: /* @__PURE__ */ jsx("div", { className: "flex flex-wrap items-center gap-2", role: "group", "aria-label": typeof label === "string" ? label : "One-time code", children: chars.map((char, idx) => /* @__PURE__ */ jsxs(React31.Fragment, { children: [
9837
+ return /* @__PURE__ */ jsx(Field, { className, label, htmlFor, errorId, errorMessage, required, layout, helperText, children: /* @__PURE__ */ jsx("div", { className: "flex flex-wrap items-center gap-2", role: "group", "aria-label": typeof label === "string" ? label : "One-time code", children: chars.map((char, idx) => /* @__PURE__ */ jsxs(React34.Fragment, { children: [
9075
9838
  /* @__PURE__ */ jsx(
9076
9839
  "input",
9077
9840
  {
@@ -10130,7 +10893,7 @@ function Blog({
10130
10893
  post.tag != null && !post.image && /* @__PURE__ */ jsx("div", { children: /* @__PURE__ */ jsx(Badge, { tone: "accent", variant: "soft", size: "sm", children: post.tag }) }),
10131
10894
  /* @__PURE__ */ jsx("h3", { className: "text-base font-semibold leading-snug text-foreground transition-colors group-hover:text-accent", children: post.title }),
10132
10895
  post.excerpt != null && /* @__PURE__ */ jsx("p", { className: "line-clamp-3 text-sm leading-relaxed text-foreground-secondary", children: post.excerpt }),
10133
- meta.length > 0 && /* @__PURE__ */ jsx("div", { className: "mt-auto flex flex-wrap items-center gap-x-2 gap-y-1 pt-3 text-xs text-foreground-muted", children: meta.map((m, j) => /* @__PURE__ */ jsxs(React31.Fragment, { children: [
10896
+ meta.length > 0 && /* @__PURE__ */ jsx("div", { className: "mt-auto flex flex-wrap items-center gap-x-2 gap-y-1 pt-3 text-xs text-foreground-muted", children: meta.map((m, j) => /* @__PURE__ */ jsxs(React34.Fragment, { children: [
10134
10897
  j > 0 && /* @__PURE__ */ jsx("span", { "aria-hidden": "true", children: "\xB7" }),
10135
10898
  /* @__PURE__ */ jsx("span", { children: m })
10136
10899
  ] }, j)) })
@@ -10303,6 +11066,6 @@ function LeadCapture({
10303
11066
  );
10304
11067
  }
10305
11068
 
10306
- export { Accordion_default as Accordion, AppShell, AutoComplete, Avatar, Badge, Banner, Blog, Box, Breadcrumbs, Button_default as Button, CARD_BRANDS, Card_default as Card, CardCarousel, Cart, CartButton, CartProvider, Catalog, CatalogCarousel, CatalogGrid, Chat, Checkbox, Checkout, ColorPicker, ContextMenu, CookieConsent, CreditCardForm, DateRangePicker, Drawer, Dropdown, EmptyCart, FAB, FadingBase, FeatureGrid, Field, FieldHelpIcon, FieldLabel, FileInput, Flex, Form, FormContext, FormField, FormStore, Grid, GridCard, IconButton_default as IconButton, Jumbotron, Kbd, LeadCapture, List2 as List, LoadingSpinner, LogoutTimer, MegaMenu_default as MegaMenu, MenuButton, Modal, NotificationProvider, NumberInput, OpaqueGridCard, OtpInput, Parallax, Password, PasswordStrength, PopConfirm, Portal, PricingPlans, RadioGroup, RadioTile, Rating, ScalableContainer, Scheduler, SearchInput_default as SearchInput, SecureLayout, SegmentedControl, Sidebar, SkeletonBox, SkeletonCard, SkeletonCircle, SkeletonText, SlideShow, Slider, Socials, Statistic, Stepper, Switch, Table, Tabs_default as Tabs, TagsInput, DatePicker as Temporal, Testimonials, TextArea, TextInput, ThemeProvider, ThemeSwitch, TimePicker, Timeline, Tooltip, TooltipProvider, TopBar, Tree, TreeSelect, Typography, Video, VirtualList, Wizard, cardNumberError, cvvError, cx, defaultPasswordRules, detectBrand, expiryError, fieldShell, formatCardNumber, formatExpiry, isRequired, luhnValid, onlyDigits, patterns, runFieldRules, scorePassword, useBreakpoint, useCart, useFieldArray, useForm, useFormField, useFormStore, useJwt, useLocalStorage, useMediaQuery, useNotification };
11069
+ export { Accordion_default as Accordion, AppShell, AutoComplete, Avatar, Badge, Banner, Blog, Box, Breadcrumbs, Button_default as Button, CARD_BRANDS, Card_default as Card, CardCarousel, Cart, CartButton, CartProvider, Catalog, CatalogCarousel, CatalogGrid, Chat, Checkbox, Checkout, ColorPicker, ContextMenu, CookieConsent, CreditCardForm, DataGrid, DateRangePicker, Drawer, Dropdown, EmptyCart, FAB, FadingBase, FeatureGrid, Field, FieldHelpIcon, FieldLabel, FileInput, Flex, Form, FormContext, FormField, FormStore, Grid, GridCard, IconButton_default as IconButton, Jumbotron, Kbd, LeadCapture, List2 as List, LoadingSpinner, LogoutTimer, MegaMenu_default as MegaMenu, MenuButton, Modal, NotificationProvider, NumberInput, OpaqueGridCard, OtpInput, Parallax, Password, PasswordStrength, PdfViewer, PopConfirm, Portal, PricingPlans, RadioGroup, RadioTile, Rating, ScalableContainer, Scheduler, SearchInput_default as SearchInput, SecureLayout, SegmentedControl, Sidebar, SkeletonBox, SkeletonCard, SkeletonCircle, SkeletonText, SlideShow, Slider, Socials, Spreadsheet, Statistic, Stepper, Switch, Table, Tabs_default as Tabs, TagsInput, DatePicker as Temporal, Testimonials, TextArea, TextInput, ThemeProvider, ThemeSwitch, TimePicker, Timeline, Tooltip, TooltipProvider, TopBar, Tree, TreeSelect, Typography, Video, VirtualList, Wizard, cardNumberError, cvvError, cx, defaultPasswordRules, detectBrand, expiryError, fieldShell, formatCardNumber, formatExpiry, isRequired, luhnValid, onlyDigits, patterns, runFieldRules, scorePassword, useBreakpoint, useCart, useFieldArray, useForm, useFormField, useFormStore, useJwt, useLocalStorage, useMediaQuery, useNotification };
10307
11070
  //# sourceMappingURL=index.js.map
10308
11071
  //# sourceMappingURL=index.js.map