@geomak/ui 7.7.3 → 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();
@@ -4655,8 +4655,8 @@ var TOGGLE_POSITION_CLASS = {
4655
4655
  "bottom-right": "bottom-2 right-2"
4656
4656
  };
4657
4657
  function ScalableContainer({
4658
- width = "100%",
4659
- height = "auto",
4658
+ width,
4659
+ height,
4660
4660
  targetWidth,
4661
4661
  targetHeight,
4662
4662
  expandedWidth = "100%",
@@ -4705,25 +4705,20 @@ function ScalableContainer({
4705
4705
  };
4706
4706
  const wrapperClass = isScaled ? assignClassOnClick : void 0;
4707
4707
  return /* @__PURE__ */ jsxs(
4708
- motion.div,
4708
+ "div",
4709
4709
  {
4710
4710
  ref: containerRef,
4711
4711
  style: {
4712
4712
  width: isScaled ? expandW : width,
4713
4713
  height: isScaled ? expandH : height,
4714
- flex: isScaled ? "none" : void 0
4715
- },
4716
- animate: { width: isScaled ? expandW : width, height: isScaled ? expandH : height },
4717
- transition: reduced ? { duration: 0 } : {
4718
- width: { type: "tween", duration: 0.32, ease: [0.16, 1, 0.3, 1] },
4719
- height: { type: "tween", duration: 0.32, ease: [0.16, 1, 0.3, 1] }
4714
+ ...isScaled ? { flex: "none" } : null
4720
4715
  },
4721
4716
  className: cx(
4722
4717
  "relative rounded-lg overflow-hidden",
4718
+ "transition-[width,height,box-shadow] duration-[320ms] ease-[cubic-bezier(0.16,1,0.3,1)] motion-reduce:transition-none",
4723
4719
  // OS-window aesthetic: subtle elevation at rest, lifted shadow +
4724
4720
  // raised stacking when expanded so it sits above neighbours.
4725
4721
  isScaled ? "z-raised shadow-2xl" : "shadow-md",
4726
- "transition-shadow duration-300",
4727
4722
  className
4728
4723
  ),
4729
4724
  children: [
@@ -5250,7 +5245,7 @@ function Wizard({
5250
5245
  ] });
5251
5246
  }
5252
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" }) });
5253
- 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) {
5254
5249
  return /* @__PURE__ */ jsx(Field, { className, label, htmlFor, layout, helperText, children: /* @__PURE__ */ jsxs(
5255
5250
  "div",
5256
5251
  {
@@ -5567,7 +5562,7 @@ function TableBody({
5567
5562
  return /* @__PURE__ */ jsx("tbody", { children: rows.map((row, i) => {
5568
5563
  const rowKey = getRowKey(row, i);
5569
5564
  const isExpanded = expanded.has(rowKey);
5570
- return /* @__PURE__ */ jsxs(React31.Fragment, { children: [
5565
+ return /* @__PURE__ */ jsxs(React34.Fragment, { children: [
5571
5566
  /* @__PURE__ */ jsxs(
5572
5567
  "tr",
5573
5568
  {
@@ -5918,6 +5913,769 @@ function VirtualList({
5918
5913
  )
5919
5914
  ] });
5920
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
+ }
5921
6679
  function ThemeSwitch({ checked, onChange, label = "Toggle dark mode", className = "" }) {
5922
6680
  const id = useId();
5923
6681
  return /* @__PURE__ */ jsx("label", { htmlFor: id, className: `flex items-center gap-2 cursor-pointer select-none ${className}`.trim(), children: /* @__PURE__ */ jsx(
@@ -6018,7 +6776,7 @@ function FlyoutItems({ items }) {
6018
6776
  }
6019
6777
  function CollapsedFlyout({ item, trigger }) {
6020
6778
  const [open, setOpen] = useState(false);
6021
- const closeTimer = React31.useRef(void 0);
6779
+ const closeTimer = React34.useRef(void 0);
6022
6780
  const openNow = () => {
6023
6781
  window.clearTimeout(closeTimer.current);
6024
6782
  setOpen(true);
@@ -6027,7 +6785,7 @@ function CollapsedFlyout({ item, trigger }) {
6027
6785
  window.clearTimeout(closeTimer.current);
6028
6786
  closeTimer.current = window.setTimeout(() => setOpen(false), 150);
6029
6787
  };
6030
- React31.useEffect(() => () => window.clearTimeout(closeTimer.current), []);
6788
+ React34.useEffect(() => () => window.clearTimeout(closeTimer.current), []);
6031
6789
  return /* @__PURE__ */ jsxs(DropdownMenu2.Root, { open, onOpenChange: setOpen, modal: false, children: [
6032
6790
  /* @__PURE__ */ jsx(
6033
6791
  DropdownMenu2.Trigger,
@@ -6319,8 +7077,8 @@ function MegaMenuLink({ href, icon, description, active, onClick, children, clas
6319
7077
  function MegaMenuFeatured({ children, className = "" }) {
6320
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 });
6321
7079
  }
6322
- var elementsOfType = (children, type) => React31.Children.toArray(children).filter(
6323
- (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
6324
7082
  );
6325
7083
  var MOBILE_CHEVRON = /* @__PURE__ */ jsx(
6326
7084
  "svg",
@@ -6357,9 +7115,9 @@ function MobileLinkRow({ link, onNavigate }) {
6357
7115
  );
6358
7116
  }
6359
7117
  function MobilePanel({ panel, onNavigate }) {
6360
- const nodes = React31.Children.toArray(panel.props.children);
7118
+ const nodes = React34.Children.toArray(panel.props.children);
6361
7119
  return /* @__PURE__ */ jsx("div", { className: "flex flex-col gap-4 px-2 pb-3 pt-1", children: nodes.map((node, i) => {
6362
- if (!React31.isValidElement(node)) return null;
7120
+ if (!React34.isValidElement(node)) return null;
6363
7121
  const el = node;
6364
7122
  if (el.type === MegaMenuSection) {
6365
7123
  const { title, children } = el.props;
@@ -6768,7 +7526,7 @@ function ThemeProvider({
6768
7526
  className = "",
6769
7527
  style
6770
7528
  }) {
6771
- const id = React31.useId().replace(/:/g, "");
7529
+ const id = React34.useId().replace(/:/g, "");
6772
7530
  const scopeClass = `geo-th-${id}`;
6773
7531
  const divRef = useRef(null);
6774
7532
  useEffect(() => {
@@ -7493,7 +8251,7 @@ function MultiTagRow({
7493
8251
  const wrap = wrapRef.current;
7494
8252
  const measure = measureRef.current;
7495
8253
  if (!wrap || !measure) return;
7496
- const GAP = 6;
8254
+ const GAP2 = 6;
7497
8255
  const recompute = () => {
7498
8256
  const avail = wrap.clientWidth;
7499
8257
  const tagEls = Array.from(measure.querySelectorAll("[data-mt]"));
@@ -7507,7 +8265,7 @@ function MultiTagRow({
7507
8265
  let used = 0;
7508
8266
  let count = 0;
7509
8267
  for (let i = 0; i < widths.length; i++) {
7510
- const w = widths[i] + (i > 0 ? GAP : 0);
8268
+ const w = widths[i] + (i > 0 ? GAP2 : 0);
7511
8269
  if (used + w <= avail) {
7512
8270
  used += w;
7513
8271
  count++;
@@ -7516,8 +8274,8 @@ function MultiTagRow({
7516
8274
  if (count < widths.length) {
7517
8275
  while (count > 0) {
7518
8276
  let t = 0;
7519
- for (let i = 0; i < count; i++) t += widths[i] + (i > 0 ? GAP : 0);
7520
- t += GAP + moreW;
8277
+ for (let i = 0; i < count; i++) t += widths[i] + (i > 0 ? GAP2 : 0);
8278
+ t += GAP2 + moreW;
7521
8279
  if (t <= avail) break;
7522
8280
  count--;
7523
8281
  }
@@ -8477,7 +9235,7 @@ function DatePicker({
8477
9235
  setOpen(false);
8478
9236
  }
8479
9237
  };
8480
- const displayValue = value ? format(value) : "";
9238
+ const displayValue2 = value ? format(value) : "";
8481
9239
  return /* @__PURE__ */ jsxs("div", { className: `flex flex-col gap-1 ${className}`.trim(), children: [
8482
9240
  /* @__PURE__ */ jsxs("div", { className: `flex ${layout === "vertical" ? "flex-col gap-1.5" : "flex-row items-start gap-3"}`, children: [
8483
9241
  /* @__PURE__ */ jsx(
@@ -8504,7 +9262,7 @@ function DatePicker({
8504
9262
  "aria-expanded": open,
8505
9263
  className: `flex items-center justify-between cursor-pointer select-none ${!style?.width ? "min-w-[200px]" : ""} ${fieldShell({ size, hasError, disabled })}`,
8506
9264
  children: [
8507
- /* @__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 }),
8508
9266
  /* @__PURE__ */ jsx(CalendarIcon, {})
8509
9267
  ]
8510
9268
  }
@@ -9076,7 +9834,7 @@ function OtpInput({
9076
9834
  emit(valid.join(""));
9077
9835
  focusBox(valid.length);
9078
9836
  };
9079
- 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: [
9080
9838
  /* @__PURE__ */ jsx(
9081
9839
  "input",
9082
9840
  {
@@ -10135,7 +10893,7 @@ function Blog({
10135
10893
  post.tag != null && !post.image && /* @__PURE__ */ jsx("div", { children: /* @__PURE__ */ jsx(Badge, { tone: "accent", variant: "soft", size: "sm", children: post.tag }) }),
10136
10894
  /* @__PURE__ */ jsx("h3", { className: "text-base font-semibold leading-snug text-foreground transition-colors group-hover:text-accent", children: post.title }),
10137
10895
  post.excerpt != null && /* @__PURE__ */ jsx("p", { className: "line-clamp-3 text-sm leading-relaxed text-foreground-secondary", children: post.excerpt }),
10138
- 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: [
10139
10897
  j > 0 && /* @__PURE__ */ jsx("span", { "aria-hidden": "true", children: "\xB7" }),
10140
10898
  /* @__PURE__ */ jsx("span", { children: m })
10141
10899
  ] }, j)) })
@@ -10308,6 +11066,6 @@ function LeadCapture({
10308
11066
  );
10309
11067
  }
10310
11068
 
10311
- 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 };
10312
11070
  //# sourceMappingURL=index.js.map
10313
11071
  //# sourceMappingURL=index.js.map