@geomak/ui 7.7.4 → 7.9.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.cjs +1270 -317
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +148 -1
- package/dist/index.d.ts +148 -1
- package/dist/index.js +975 -25
- package/dist/index.js.map +1 -1
- package/dist/styles.css +19 -0
- package/package.json +5 -2
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
|
|
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 =
|
|
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 =
|
|
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(
|
|
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(
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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(
|
|
5565
|
+
return /* @__PURE__ */ jsxs(React34.Fragment, { children: [
|
|
5566
5566
|
/* @__PURE__ */ jsxs(
|
|
5567
5567
|
"tr",
|
|
5568
5568
|
{
|
|
@@ -5913,6 +5913,956 @@ 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 downloadBlob(blob, name) {
|
|
5941
|
+
if (typeof document === "undefined") return;
|
|
5942
|
+
const url = URL.createObjectURL(blob);
|
|
5943
|
+
const a = document.createElement("a");
|
|
5944
|
+
a.href = url;
|
|
5945
|
+
a.download = name;
|
|
5946
|
+
a.click();
|
|
5947
|
+
setTimeout(() => URL.revokeObjectURL(url), 1e3);
|
|
5948
|
+
}
|
|
5949
|
+
function sourceName(source) {
|
|
5950
|
+
if (typeof File !== "undefined" && source instanceof File) return source.name;
|
|
5951
|
+
if (isUrlSource(source)) {
|
|
5952
|
+
const href = urlHref(source);
|
|
5953
|
+
const last = href.split("?")[0].split("#")[0].split("/").filter(Boolean).pop();
|
|
5954
|
+
return last || void 0;
|
|
5955
|
+
}
|
|
5956
|
+
return void 0;
|
|
5957
|
+
}
|
|
5958
|
+
var pdfjsPromise = null;
|
|
5959
|
+
function loadPdfjs(workerSrc) {
|
|
5960
|
+
if (pdfjsPromise) return pdfjsPromise;
|
|
5961
|
+
pdfjsPromise = import('pdfjs-dist').then((pdfjs) => {
|
|
5962
|
+
if (!pdfjs.GlobalWorkerOptions.workerSrc) {
|
|
5963
|
+
pdfjs.GlobalWorkerOptions.workerSrc = workerSrc ?? `https://cdn.jsdelivr.net/npm/pdfjs-dist@${pdfjs.version}/build/pdf.worker.min.mjs`;
|
|
5964
|
+
}
|
|
5965
|
+
return pdfjs;
|
|
5966
|
+
});
|
|
5967
|
+
return pdfjsPromise;
|
|
5968
|
+
}
|
|
5969
|
+
var GAP = 12;
|
|
5970
|
+
var SEARCH_DEBOUNCE = 220;
|
|
5971
|
+
var TEXT_LAYER_STYLE_ID = "oxygen-pdf-textlayer";
|
|
5972
|
+
var TEXT_LAYER_CSS = `
|
|
5973
|
+
.oxy-textLayer{position:absolute;inset:0;overflow:clip;line-height:1;text-size-adjust:none;forced-color-adjust:none;transform-origin:0 0}
|
|
5974
|
+
.oxy-textLayer span,.oxy-textLayer br{color:transparent;position:absolute;white-space:pre;cursor:text;transform-origin:0 0}
|
|
5975
|
+
.oxy-textLayer span.markedContent{top:0;height:0}
|
|
5976
|
+
.oxy-textLayer ::selection{background:color-mix(in srgb, var(--color-accent) 35%, transparent)}
|
|
5977
|
+
`;
|
|
5978
|
+
function ensureTextLayerStyles() {
|
|
5979
|
+
if (typeof document === "undefined" || document.getElementById(TEXT_LAYER_STYLE_ID)) return;
|
|
5980
|
+
const el = document.createElement("style");
|
|
5981
|
+
el.id = TEXT_LAYER_STYLE_ID;
|
|
5982
|
+
el.textContent = TEXT_LAYER_CSS;
|
|
5983
|
+
document.head.appendChild(el);
|
|
5984
|
+
}
|
|
5985
|
+
function pageIndexAt(offsets, y) {
|
|
5986
|
+
let lo = 0;
|
|
5987
|
+
let hi = offsets.length - 1;
|
|
5988
|
+
let ans = 0;
|
|
5989
|
+
while (lo <= hi) {
|
|
5990
|
+
const mid = lo + hi >> 1;
|
|
5991
|
+
if (offsets[mid] <= y) {
|
|
5992
|
+
ans = mid;
|
|
5993
|
+
lo = mid + 1;
|
|
5994
|
+
} else hi = mid - 1;
|
|
5995
|
+
}
|
|
5996
|
+
return ans;
|
|
5997
|
+
}
|
|
5998
|
+
function PdfViewer({
|
|
5999
|
+
source,
|
|
6000
|
+
remote,
|
|
6001
|
+
initialPage = 1,
|
|
6002
|
+
zoom = "page-width",
|
|
6003
|
+
toolbar = true,
|
|
6004
|
+
thumbnails = false,
|
|
6005
|
+
textLayer = true,
|
|
6006
|
+
workerSrc,
|
|
6007
|
+
onLoad,
|
|
6008
|
+
onError,
|
|
6009
|
+
onPageChange,
|
|
6010
|
+
className = "",
|
|
6011
|
+
style
|
|
6012
|
+
}) {
|
|
6013
|
+
const [pdfjs, setPdfjs] = useState(null);
|
|
6014
|
+
const [doc, setDoc] = useState(null);
|
|
6015
|
+
const [numPages, setNumPages] = useState(0);
|
|
6016
|
+
const [baseSize, setBaseSize] = useState(null);
|
|
6017
|
+
const [sizes, setSizes] = useState([]);
|
|
6018
|
+
const [status, setStatus] = useState("loading");
|
|
6019
|
+
const [error, setError] = useState(null);
|
|
6020
|
+
const [reloadKey, setReloadKey] = useState(0);
|
|
6021
|
+
const scrollRef = useRef(null);
|
|
6022
|
+
const [scrollTop, setScrollTop] = useState(0);
|
|
6023
|
+
const [viewport, setViewport] = useState({ w: 0, h: 0 });
|
|
6024
|
+
const [zoomMode, setZoomMode] = useState(zoom);
|
|
6025
|
+
const [page, setPage] = useState(initialPage);
|
|
6026
|
+
const [showSearch, setShowSearch] = useState(false);
|
|
6027
|
+
const [query, setQuery] = useState("");
|
|
6028
|
+
const [matchPages, setMatchPages] = useState(null);
|
|
6029
|
+
const [matchIdx, setMatchIdx] = useState(0);
|
|
6030
|
+
const [searching, setSearching] = useState(false);
|
|
6031
|
+
const [pageDraft, setPageDraft] = useState("");
|
|
6032
|
+
const pageEditing = useRef(false);
|
|
6033
|
+
const pageText = useRef(/* @__PURE__ */ new Map());
|
|
6034
|
+
const searchSeq = useRef(0);
|
|
6035
|
+
const searchTimer = useRef(null);
|
|
6036
|
+
const measured = useRef(/* @__PURE__ */ new Set());
|
|
6037
|
+
const tb = toolbar === true ? { zoom: true, pager: true, download: true, print: true, search: true } : toolbar || {};
|
|
6038
|
+
useEffect(() => {
|
|
6039
|
+
let cancelled = false;
|
|
6040
|
+
let task;
|
|
6041
|
+
setStatus("loading");
|
|
6042
|
+
setError(null);
|
|
6043
|
+
setDoc(null);
|
|
6044
|
+
setBaseSize(null);
|
|
6045
|
+
setSizes([]);
|
|
6046
|
+
pageText.current.clear();
|
|
6047
|
+
measured.current = /* @__PURE__ */ new Set();
|
|
6048
|
+
setMatchPages(null);
|
|
6049
|
+
setMatchIdx(0);
|
|
6050
|
+
loadPdfjs(workerSrc).then(async (pdfjs2) => {
|
|
6051
|
+
if (cancelled) return;
|
|
6052
|
+
setPdfjs(pdfjs2);
|
|
6053
|
+
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()) };
|
|
6054
|
+
task = pdfjs2.getDocument(params);
|
|
6055
|
+
const pdf = await task.promise;
|
|
6056
|
+
if (cancelled) {
|
|
6057
|
+
pdf.destroy?.();
|
|
6058
|
+
return;
|
|
6059
|
+
}
|
|
6060
|
+
const first = await pdf.getPage(1);
|
|
6061
|
+
const vp = first.getViewport({ scale: 1 });
|
|
6062
|
+
if (cancelled) return;
|
|
6063
|
+
const base = { width: vp.width, height: vp.height };
|
|
6064
|
+
setDoc(pdf);
|
|
6065
|
+
setNumPages(pdf.numPages);
|
|
6066
|
+
setBaseSize(base);
|
|
6067
|
+
setSizes(Array.from({ length: pdf.numPages }, () => base));
|
|
6068
|
+
measured.current.add(1);
|
|
6069
|
+
setStatus("ready");
|
|
6070
|
+
onLoad?.({ numPages: pdf.numPages });
|
|
6071
|
+
}).catch((err) => {
|
|
6072
|
+
if (cancelled) return;
|
|
6073
|
+
setStatus("error");
|
|
6074
|
+
setError(err);
|
|
6075
|
+
onError?.(err);
|
|
6076
|
+
});
|
|
6077
|
+
return () => {
|
|
6078
|
+
cancelled = true;
|
|
6079
|
+
task?.destroy?.();
|
|
6080
|
+
};
|
|
6081
|
+
}, [source, remote, reloadKey, workerSrc]);
|
|
6082
|
+
useEffect(() => () => {
|
|
6083
|
+
doc?.destroy?.();
|
|
6084
|
+
}, [doc]);
|
|
6085
|
+
useEffect(() => {
|
|
6086
|
+
const el = scrollRef.current;
|
|
6087
|
+
if (!el || typeof ResizeObserver === "undefined") return;
|
|
6088
|
+
const measure = () => setViewport({ w: el.clientWidth, h: el.clientHeight });
|
|
6089
|
+
measure();
|
|
6090
|
+
const ro = new ResizeObserver(measure);
|
|
6091
|
+
ro.observe(el);
|
|
6092
|
+
return () => ro.disconnect();
|
|
6093
|
+
}, [status]);
|
|
6094
|
+
const handleMeasure = useCallback((p, size) => {
|
|
6095
|
+
setSizes((prev) => {
|
|
6096
|
+
const cur = prev[p - 1];
|
|
6097
|
+
if (cur && Math.abs(cur.width - size.width) < 0.5 && Math.abs(cur.height - size.height) < 0.5) return prev;
|
|
6098
|
+
const next = prev.slice();
|
|
6099
|
+
next[p - 1] = size;
|
|
6100
|
+
return next;
|
|
6101
|
+
});
|
|
6102
|
+
}, []);
|
|
6103
|
+
const scale = useMemo(() => {
|
|
6104
|
+
if (!baseSize) return 1;
|
|
6105
|
+
if (typeof zoomMode === "number") return zoomMode;
|
|
6106
|
+
const avail = Math.max(1, (viewport.w || baseSize.width) - 32);
|
|
6107
|
+
if (zoomMode === "page-width" || zoomMode === "auto") return avail / baseSize.width;
|
|
6108
|
+
if (zoomMode === "page-fit") {
|
|
6109
|
+
const availH = Math.max(1, (viewport.h || baseSize.height) - 32);
|
|
6110
|
+
return Math.min(avail / baseSize.width, availH / baseSize.height);
|
|
6111
|
+
}
|
|
6112
|
+
return 1;
|
|
6113
|
+
}, [zoomMode, baseSize, viewport]);
|
|
6114
|
+
const { offsets, total } = useMemo(() => {
|
|
6115
|
+
const offs = new Array(sizes.length);
|
|
6116
|
+
let acc = 0;
|
|
6117
|
+
for (let i = 0; i < sizes.length; i++) {
|
|
6118
|
+
offs[i] = acc;
|
|
6119
|
+
acc += sizes[i].height * scale + GAP;
|
|
6120
|
+
}
|
|
6121
|
+
return { offsets: offs, total: acc };
|
|
6122
|
+
}, [sizes, scale]);
|
|
6123
|
+
const overscan = 1;
|
|
6124
|
+
const hasLayout = offsets.length > 0 && total > 0;
|
|
6125
|
+
const startIdx = hasLayout ? Math.max(0, pageIndexAt(offsets, scrollTop) - overscan) : 0;
|
|
6126
|
+
const endIdx = hasLayout ? Math.min(numPages, pageIndexAt(offsets, scrollTop + viewport.h) + 1 + overscan) : 0;
|
|
6127
|
+
const visiblePages = Array.from({ length: Math.max(0, endIdx - startIdx) }, (_, i) => startIdx + i + 1);
|
|
6128
|
+
const scrollToPage = useCallback((p) => {
|
|
6129
|
+
const el = scrollRef.current;
|
|
6130
|
+
if (!el || !offsets.length) return;
|
|
6131
|
+
const clamped = Math.min(offsets.length, Math.max(1, p));
|
|
6132
|
+
el.scrollTo({ top: offsets[clamped - 1] ?? 0, behavior: "smooth" });
|
|
6133
|
+
}, [offsets]);
|
|
6134
|
+
useEffect(() => {
|
|
6135
|
+
if (!hasLayout) return;
|
|
6136
|
+
const cur = pageIndexAt(offsets, scrollTop + viewport.h / 2) + 1;
|
|
6137
|
+
if (cur !== page) {
|
|
6138
|
+
setPage(cur);
|
|
6139
|
+
onPageChange?.(cur);
|
|
6140
|
+
}
|
|
6141
|
+
}, [scrollTop, offsets, viewport.h, numPages]);
|
|
6142
|
+
useEffect(() => {
|
|
6143
|
+
if (status === "ready" && initialPage > 1) scrollToPage(initialPage);
|
|
6144
|
+
}, [status, initialPage]);
|
|
6145
|
+
const runSearch = useCallback(async (q) => {
|
|
6146
|
+
const needle = q.trim().toLowerCase();
|
|
6147
|
+
if (!doc || !needle) {
|
|
6148
|
+
setMatchPages(null);
|
|
6149
|
+
setMatchIdx(0);
|
|
6150
|
+
setSearching(false);
|
|
6151
|
+
return;
|
|
6152
|
+
}
|
|
6153
|
+
const seq = ++searchSeq.current;
|
|
6154
|
+
setSearching(true);
|
|
6155
|
+
const hits = [];
|
|
6156
|
+
for (let p = 1; p <= numPages; p++) {
|
|
6157
|
+
let text = pageText.current.get(p);
|
|
6158
|
+
if (text === void 0) {
|
|
6159
|
+
const pg = await doc.getPage(p);
|
|
6160
|
+
const tc = await pg.getTextContent();
|
|
6161
|
+
const built = tc.items.map((it) => "str" in it ? it.str : "").join(" ").toLowerCase();
|
|
6162
|
+
pageText.current.set(p, built);
|
|
6163
|
+
text = built;
|
|
6164
|
+
}
|
|
6165
|
+
if (seq !== searchSeq.current) return;
|
|
6166
|
+
if (text.includes(needle)) hits.push(p);
|
|
6167
|
+
}
|
|
6168
|
+
if (seq !== searchSeq.current) return;
|
|
6169
|
+
setMatchPages(hits);
|
|
6170
|
+
setMatchIdx(0);
|
|
6171
|
+
setSearching(false);
|
|
6172
|
+
if (hits.length) scrollToPage(hits[0]);
|
|
6173
|
+
}, [doc, numPages, scrollToPage]);
|
|
6174
|
+
const onQueryChange = useCallback((v) => {
|
|
6175
|
+
setQuery(v);
|
|
6176
|
+
if (searchTimer.current) clearTimeout(searchTimer.current);
|
|
6177
|
+
if (!v.trim()) {
|
|
6178
|
+
searchSeq.current++;
|
|
6179
|
+
setMatchPages(null);
|
|
6180
|
+
setMatchIdx(0);
|
|
6181
|
+
setSearching(false);
|
|
6182
|
+
return;
|
|
6183
|
+
}
|
|
6184
|
+
searchTimer.current = setTimeout(() => runSearch(v), SEARCH_DEBOUNCE);
|
|
6185
|
+
}, [runSearch]);
|
|
6186
|
+
useEffect(() => () => {
|
|
6187
|
+
if (searchTimer.current) clearTimeout(searchTimer.current);
|
|
6188
|
+
}, []);
|
|
6189
|
+
const gotoMatch = (dir) => {
|
|
6190
|
+
if (!matchPages?.length) return;
|
|
6191
|
+
const next = (matchIdx + dir + matchPages.length) % matchPages.length;
|
|
6192
|
+
setMatchIdx(next);
|
|
6193
|
+
scrollToPage(matchPages[next]);
|
|
6194
|
+
};
|
|
6195
|
+
const getBytes = useCallback(async () => {
|
|
6196
|
+
if (doc?.getData) return doc.getData();
|
|
6197
|
+
if (source instanceof Uint8Array) return source;
|
|
6198
|
+
if (source instanceof ArrayBuffer) return new Uint8Array(source);
|
|
6199
|
+
if (typeof Blob !== "undefined" && source instanceof Blob) return new Uint8Array(await source.arrayBuffer());
|
|
6200
|
+
throw new Error("No bytes available");
|
|
6201
|
+
}, [doc, source]);
|
|
6202
|
+
const download = useCallback(async () => {
|
|
6203
|
+
const bytes = await getBytes();
|
|
6204
|
+
downloadBlob(new Blob([bytes], { type: "application/pdf" }), sourceName(source) || "document.pdf");
|
|
6205
|
+
}, [getBytes, source]);
|
|
6206
|
+
const print = useCallback(async () => {
|
|
6207
|
+
const bytes = await getBytes();
|
|
6208
|
+
const blob = new Blob([bytes], { type: "application/pdf" });
|
|
6209
|
+
const url = URL.createObjectURL(blob);
|
|
6210
|
+
const iframe = document.createElement("iframe");
|
|
6211
|
+
iframe.style.cssText = "position:fixed;right:0;bottom:0;width:0;height:0;border:0";
|
|
6212
|
+
iframe.src = url;
|
|
6213
|
+
iframe.onload = () => {
|
|
6214
|
+
try {
|
|
6215
|
+
iframe.contentWindow?.focus();
|
|
6216
|
+
iframe.contentWindow?.print();
|
|
6217
|
+
} catch {
|
|
6218
|
+
}
|
|
6219
|
+
};
|
|
6220
|
+
document.body.appendChild(iframe);
|
|
6221
|
+
setTimeout(() => {
|
|
6222
|
+
document.body.removeChild(iframe);
|
|
6223
|
+
URL.revokeObjectURL(url);
|
|
6224
|
+
}, 6e4);
|
|
6225
|
+
}, [getBytes]);
|
|
6226
|
+
const setZoomNum = (factor) => {
|
|
6227
|
+
const cur = typeof zoomMode === "number" ? zoomMode : scale;
|
|
6228
|
+
setZoomMode(Math.min(5, Math.max(0.25, +(cur * factor).toFixed(2))));
|
|
6229
|
+
};
|
|
6230
|
+
const zoomPct = Math.round(scale * 100);
|
|
6231
|
+
const commitPageDraft = () => {
|
|
6232
|
+
pageEditing.current = false;
|
|
6233
|
+
const n = parseInt(pageDraft, 10);
|
|
6234
|
+
if (Number.isFinite(n)) scrollToPage(n);
|
|
6235
|
+
};
|
|
6236
|
+
if (status === "error") {
|
|
6237
|
+
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: [
|
|
6238
|
+
/* @__PURE__ */ jsx("p", { className: "text-sm font-medium text-status-error", children: "Couldn\u2019t load the PDF" }),
|
|
6239
|
+
error?.message && /* @__PURE__ */ jsx("p", { className: "max-w-md text-xs text-foreground-muted", children: error.message }),
|
|
6240
|
+
/* @__PURE__ */ jsx(Button_default, { content: "Retry", size: "sm", variant: "outline", onClick: () => setReloadKey((k) => k + 1) })
|
|
6241
|
+
] });
|
|
6242
|
+
}
|
|
6243
|
+
const ready = status === "ready";
|
|
6244
|
+
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: [
|
|
6245
|
+
toolbar !== false && /* @__PURE__ */ jsxs("div", { className: "flex flex-shrink-0 flex-col border-b border-border bg-surface", children: [
|
|
6246
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1 px-2 py-1.5", children: [
|
|
6247
|
+
tb.pager && /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1", children: [
|
|
6248
|
+
/* @__PURE__ */ jsx(IconButton_default, { type: "ghost", size: "sm", title: "Previous page", onClick: () => scrollToPage(page - 1), icon: /* @__PURE__ */ jsx(Chevron5, { dir: "up" }), disabled: !ready || page <= 1 }),
|
|
6249
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1 text-xs text-foreground-secondary select-none", children: [
|
|
6250
|
+
/* @__PURE__ */ jsx(
|
|
6251
|
+
"input",
|
|
6252
|
+
{
|
|
6253
|
+
value: pageEditing.current ? pageDraft : ready ? String(page) : "\u2013",
|
|
6254
|
+
onFocus: () => {
|
|
6255
|
+
pageEditing.current = true;
|
|
6256
|
+
setPageDraft(String(page));
|
|
6257
|
+
},
|
|
6258
|
+
onChange: (e) => setPageDraft(e.target.value.replace(/[^\d]/g, "")),
|
|
6259
|
+
onBlur: commitPageDraft,
|
|
6260
|
+
onKeyDown: (e) => {
|
|
6261
|
+
if (e.key === "Enter") e.target.blur();
|
|
6262
|
+
},
|
|
6263
|
+
disabled: !ready,
|
|
6264
|
+
"aria-label": "Page number",
|
|
6265
|
+
className: "h-6 w-9 rounded border border-border bg-surface-raised text-center tabular-nums text-foreground outline-none focus:border-accent disabled:opacity-50"
|
|
6266
|
+
}
|
|
6267
|
+
),
|
|
6268
|
+
/* @__PURE__ */ jsxs("span", { className: "tabular-nums", children: [
|
|
6269
|
+
"/ ",
|
|
6270
|
+
numPages || "\u2013"
|
|
6271
|
+
] })
|
|
6272
|
+
] }),
|
|
6273
|
+
/* @__PURE__ */ jsx(IconButton_default, { type: "ghost", size: "sm", title: "Next page", onClick: () => scrollToPage(page + 1), icon: /* @__PURE__ */ jsx(Chevron5, { dir: "down" }), disabled: !ready || page >= numPages })
|
|
6274
|
+
] }),
|
|
6275
|
+
tb.pager && tb.zoom && /* @__PURE__ */ jsx("span", { className: "mx-1 h-5 w-px bg-border", "aria-hidden": "true" }),
|
|
6276
|
+
tb.zoom && /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1", children: [
|
|
6277
|
+
/* @__PURE__ */ jsx(IconButton_default, { type: "ghost", size: "sm", title: "Zoom out", onClick: () => setZoomNum(1 / 1.2), icon: /* @__PURE__ */ jsx(MinusIcon, {}), disabled: !ready }),
|
|
6278
|
+
/* @__PURE__ */ jsx("span", { className: "w-10 text-center text-xs tabular-nums text-foreground-secondary select-none", children: ready ? `${zoomPct}%` : "\u2013" }),
|
|
6279
|
+
/* @__PURE__ */ jsx(IconButton_default, { type: "ghost", size: "sm", title: "Zoom in", onClick: () => setZoomNum(1.2), icon: /* @__PURE__ */ jsx(PlusIcon, {}), disabled: !ready }),
|
|
6280
|
+
/* @__PURE__ */ jsx(IconButton_default, { type: "ghost", size: "sm", title: "Fit width", onClick: () => setZoomMode("page-width"), icon: /* @__PURE__ */ jsx(FitWidthIcon, {}), disabled: !ready }),
|
|
6281
|
+
/* @__PURE__ */ jsx(IconButton_default, { type: "ghost", size: "sm", title: "Fit page", onClick: () => setZoomMode("page-fit"), icon: /* @__PURE__ */ jsx(FitPageIcon, {}), disabled: !ready })
|
|
6282
|
+
] }),
|
|
6283
|
+
/* @__PURE__ */ jsxs("div", { className: "ml-auto flex items-center gap-1", children: [
|
|
6284
|
+
tb.search && /* @__PURE__ */ jsx(IconButton_default, { type: showSearch ? "bordered" : "ghost", size: "sm", title: "Search", onClick: () => setShowSearch((s) => !s), icon: /* @__PURE__ */ jsx(SearchIcon2, {}), disabled: !ready }),
|
|
6285
|
+
tb.search && (tb.download || tb.print) && /* @__PURE__ */ jsx("span", { className: "mx-1 h-5 w-px bg-border", "aria-hidden": "true" }),
|
|
6286
|
+
tb.download && /* @__PURE__ */ jsx(IconButton_default, { type: "ghost", size: "sm", title: "Download", onClick: download, icon: /* @__PURE__ */ jsx(DownloadIcon, {}), disabled: !ready }),
|
|
6287
|
+
tb.print && /* @__PURE__ */ jsx(IconButton_default, { type: "ghost", size: "sm", title: "Print", onClick: print, icon: /* @__PURE__ */ jsx(PrintIcon, {}), disabled: !ready })
|
|
6288
|
+
] })
|
|
6289
|
+
] }),
|
|
6290
|
+
tb.search && showSearch && /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 border-t border-border px-2 py-1.5", children: [
|
|
6291
|
+
/* @__PURE__ */ jsx(SearchIcon2, {}),
|
|
6292
|
+
/* @__PURE__ */ jsx(
|
|
6293
|
+
"input",
|
|
6294
|
+
{
|
|
6295
|
+
autoFocus: true,
|
|
6296
|
+
value: query,
|
|
6297
|
+
onChange: (e) => onQueryChange(e.target.value),
|
|
6298
|
+
onKeyDown: (e) => {
|
|
6299
|
+
if (e.key === "Enter") gotoMatch(e.shiftKey ? -1 : 1);
|
|
6300
|
+
},
|
|
6301
|
+
placeholder: "Find in document\u2026",
|
|
6302
|
+
className: "h-7 flex-1 bg-transparent text-sm text-foreground outline-none placeholder:text-foreground-muted"
|
|
6303
|
+
}
|
|
6304
|
+
),
|
|
6305
|
+
/* @__PURE__ */ jsx("span", { className: "text-xs tabular-nums text-foreground-muted", children: searching ? "Searching\u2026" : matchPages == null ? "" : matchPages.length ? `${matchIdx + 1} of ${matchPages.length}` : "No matches" }),
|
|
6306
|
+
/* @__PURE__ */ jsx(IconButton_default, { type: "ghost", size: "sm", title: "Previous match", onClick: () => gotoMatch(-1), icon: /* @__PURE__ */ jsx(Chevron5, { dir: "up" }), disabled: !matchPages?.length }),
|
|
6307
|
+
/* @__PURE__ */ jsx(IconButton_default, { type: "ghost", size: "sm", title: "Next match", onClick: () => gotoMatch(1), icon: /* @__PURE__ */ jsx(Chevron5, { dir: "down" }), disabled: !matchPages?.length }),
|
|
6308
|
+
/* @__PURE__ */ jsx(IconButton_default, { type: "ghost", size: "sm", title: "Close search", onClick: () => {
|
|
6309
|
+
setShowSearch(false);
|
|
6310
|
+
onQueryChange("");
|
|
6311
|
+
}, icon: /* @__PURE__ */ jsx(CloseIcon, {}) })
|
|
6312
|
+
] })
|
|
6313
|
+
] }),
|
|
6314
|
+
/* @__PURE__ */ jsxs("div", { className: "flex min-h-0 flex-1", children: [
|
|
6315
|
+
thumbnails && 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) => {
|
|
6316
|
+
const s = sizes[p - 1] ?? baseSize;
|
|
6317
|
+
return /* @__PURE__ */ jsx(
|
|
6318
|
+
"button",
|
|
6319
|
+
{
|
|
6320
|
+
type: "button",
|
|
6321
|
+
onClick: () => scrollToPage(p),
|
|
6322
|
+
className: cx(
|
|
6323
|
+
"mb-2 block w-full overflow-hidden rounded border bg-surface-raised transition-colors",
|
|
6324
|
+
p === page ? "border-accent ring-1 ring-accent" : "border-border hover:border-border-strong"
|
|
6325
|
+
),
|
|
6326
|
+
style: { aspectRatio: `${s.width} / ${s.height}` },
|
|
6327
|
+
"aria-label": `Page ${p}`,
|
|
6328
|
+
"aria-current": p === page,
|
|
6329
|
+
children: Math.abs(p - page) <= 8 ? /* @__PURE__ */ jsx(PdfPage, { pdfjs, doc, page: p, scale: 112 / s.width, textLayer: false }) : /* @__PURE__ */ jsx("span", { className: "block py-4 text-center text-xs text-foreground-muted", children: p })
|
|
6330
|
+
},
|
|
6331
|
+
p
|
|
6332
|
+
);
|
|
6333
|
+
}) }),
|
|
6334
|
+
/* @__PURE__ */ jsx(
|
|
6335
|
+
"div",
|
|
6336
|
+
{
|
|
6337
|
+
ref: scrollRef,
|
|
6338
|
+
onScroll: (e) => setScrollTop(e.currentTarget.scrollTop),
|
|
6339
|
+
className: "relative flex-1 overflow-auto bg-background",
|
|
6340
|
+
children: !ready || !baseSize ? /* @__PURE__ */ jsx("div", { className: "flex flex-col items-center gap-3 p-6", children: /* @__PURE__ */ jsx(SkeletonBox, { width: Math.min((viewport.w || 528) - 48, 560), 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) => {
|
|
6341
|
+
const s = sizes[p - 1] ?? baseSize;
|
|
6342
|
+
return /* @__PURE__ */ jsx(
|
|
6343
|
+
"div",
|
|
6344
|
+
{
|
|
6345
|
+
style: { position: "absolute", top: offsets[p - 1], left: 0, right: 0, display: "flex", justifyContent: "center", paddingTop: GAP / 2 },
|
|
6346
|
+
children: /* @__PURE__ */ jsx("div", { className: "relative shadow-md", style: { width: s.width * scale, height: s.height * scale }, children: /* @__PURE__ */ jsx(PdfPage, { pdfjs, doc, page: p, scale, textLayer, onMeasure: handleMeasure }) })
|
|
6347
|
+
},
|
|
6348
|
+
p
|
|
6349
|
+
);
|
|
6350
|
+
}) })
|
|
6351
|
+
}
|
|
6352
|
+
)
|
|
6353
|
+
] })
|
|
6354
|
+
] });
|
|
6355
|
+
}
|
|
6356
|
+
function PdfPage({
|
|
6357
|
+
pdfjs,
|
|
6358
|
+
doc,
|
|
6359
|
+
page,
|
|
6360
|
+
scale,
|
|
6361
|
+
textLayer,
|
|
6362
|
+
onMeasure
|
|
6363
|
+
}) {
|
|
6364
|
+
const canvasRef = useRef(null);
|
|
6365
|
+
const textRef = useRef(null);
|
|
6366
|
+
useEffect(() => {
|
|
6367
|
+
let cancelled = false;
|
|
6368
|
+
let renderTask;
|
|
6369
|
+
(async () => {
|
|
6370
|
+
const pg = await doc.getPage(page);
|
|
6371
|
+
if (cancelled) return;
|
|
6372
|
+
const viewport = pg.getViewport({ scale });
|
|
6373
|
+
onMeasure?.(page, { width: viewport.width / scale, height: viewport.height / scale });
|
|
6374
|
+
const canvas = canvasRef.current;
|
|
6375
|
+
if (!canvas) return;
|
|
6376
|
+
const ratio = typeof window !== "undefined" ? window.devicePixelRatio || 1 : 1;
|
|
6377
|
+
canvas.width = Math.floor(viewport.width * ratio);
|
|
6378
|
+
canvas.height = Math.floor(viewport.height * ratio);
|
|
6379
|
+
canvas.style.width = `${Math.floor(viewport.width)}px`;
|
|
6380
|
+
canvas.style.height = `${Math.floor(viewport.height)}px`;
|
|
6381
|
+
const ctx = canvas.getContext("2d");
|
|
6382
|
+
renderTask = pg.render({ canvasContext: ctx, viewport, transform: ratio !== 1 ? [ratio, 0, 0, ratio, 0, 0] : void 0 });
|
|
6383
|
+
try {
|
|
6384
|
+
await renderTask.promise;
|
|
6385
|
+
} catch {
|
|
6386
|
+
return;
|
|
6387
|
+
}
|
|
6388
|
+
if (cancelled || !textLayer || !textRef.current || !pdfjs?.TextLayer) return;
|
|
6389
|
+
try {
|
|
6390
|
+
ensureTextLayerStyles();
|
|
6391
|
+
textRef.current.innerHTML = "";
|
|
6392
|
+
textRef.current.style.setProperty("--scale-factor", String(scale));
|
|
6393
|
+
const tl = new pdfjs.TextLayer({ textContentSource: pg.streamTextContent(), container: textRef.current, viewport });
|
|
6394
|
+
await tl.render();
|
|
6395
|
+
} catch {
|
|
6396
|
+
}
|
|
6397
|
+
})();
|
|
6398
|
+
return () => {
|
|
6399
|
+
cancelled = true;
|
|
6400
|
+
renderTask?.cancel?.();
|
|
6401
|
+
};
|
|
6402
|
+
}, [pdfjs, doc, page, scale, textLayer, onMeasure]);
|
|
6403
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
6404
|
+
/* @__PURE__ */ jsx("canvas", { ref: canvasRef, className: "block bg-white" }),
|
|
6405
|
+
textLayer && /* @__PURE__ */ jsx("div", { ref: textRef, className: "oxy-textLayer", "aria-hidden": "true" })
|
|
6406
|
+
] });
|
|
6407
|
+
}
|
|
6408
|
+
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" }) });
|
|
6409
|
+
var MinusIcon = () => /* @__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", d: "M5 12h14" }) });
|
|
6410
|
+
var PlusIcon = () => /* @__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", d: "M12 5v14M5 12h14" }) });
|
|
6411
|
+
var SearchIcon2 = () => /* @__PURE__ */ jsxs("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: 2, className: "h-4 w-4 text-foreground-muted", "aria-hidden": "true", children: [
|
|
6412
|
+
/* @__PURE__ */ jsx("circle", { cx: "11", cy: "11", r: "7" }),
|
|
6413
|
+
/* @__PURE__ */ jsx("path", { strokeLinecap: "round", d: "m21 21-4.3-4.3" })
|
|
6414
|
+
] });
|
|
6415
|
+
var CloseIcon = () => /* @__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", d: "M6 6l12 12M18 6 6 18" }) });
|
|
6416
|
+
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" }) });
|
|
6417
|
+
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" }) });
|
|
6418
|
+
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" }) });
|
|
6419
|
+
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: [
|
|
6420
|
+
/* @__PURE__ */ jsx("rect", { x: "5", y: "3", width: "14", height: "18", rx: "1" }),
|
|
6421
|
+
/* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M9 8h6M9 12h6M9 16h4" })
|
|
6422
|
+
] });
|
|
6423
|
+
var DEFAULT_COL_WIDTH = 140;
|
|
6424
|
+
var GUTTER = 52;
|
|
6425
|
+
function resolveWidth(w) {
|
|
6426
|
+
if (typeof w === "number" && Number.isFinite(w)) return w;
|
|
6427
|
+
if (typeof w === "string") {
|
|
6428
|
+
const m = /^\s*(\d+(?:\.\d+)?)(px)?\s*$/.exec(w);
|
|
6429
|
+
if (m) return parseFloat(m[1]);
|
|
6430
|
+
}
|
|
6431
|
+
return DEFAULT_COL_WIDTH;
|
|
6432
|
+
}
|
|
6433
|
+
function displayValue(v) {
|
|
6434
|
+
if (v == null) return "";
|
|
6435
|
+
return String(v);
|
|
6436
|
+
}
|
|
6437
|
+
function DataGrid({
|
|
6438
|
+
columns,
|
|
6439
|
+
rows,
|
|
6440
|
+
rowHeight = 34,
|
|
6441
|
+
headerHeight = 38,
|
|
6442
|
+
height = 480,
|
|
6443
|
+
editable = false,
|
|
6444
|
+
virtualize = true,
|
|
6445
|
+
overscan = 4,
|
|
6446
|
+
rowNumbers = true,
|
|
6447
|
+
onCellEdit,
|
|
6448
|
+
className = "",
|
|
6449
|
+
style,
|
|
6450
|
+
emptyState = "No data"
|
|
6451
|
+
}) {
|
|
6452
|
+
const scrollRef = useRef(null);
|
|
6453
|
+
const [scroll, setScroll] = useState({ top: 0, left: 0 });
|
|
6454
|
+
const [viewport, setViewport] = useState({ w: 0, h: 0 });
|
|
6455
|
+
const [editing, setEditing] = useState(null);
|
|
6456
|
+
const [draft, setDraft] = useState("");
|
|
6457
|
+
const gutter = rowNumbers ? GUTTER : 0;
|
|
6458
|
+
const { widths, offsets, totalWidth } = useMemo(() => {
|
|
6459
|
+
const widths2 = columns.map((c) => resolveWidth(c.width));
|
|
6460
|
+
const offsets2 = [];
|
|
6461
|
+
let acc = 0;
|
|
6462
|
+
for (const w of widths2) {
|
|
6463
|
+
offsets2.push(acc);
|
|
6464
|
+
acc += w;
|
|
6465
|
+
}
|
|
6466
|
+
return { widths: widths2, offsets: offsets2, totalWidth: acc };
|
|
6467
|
+
}, [columns]);
|
|
6468
|
+
const totalHeight = rows.length * rowHeight;
|
|
6469
|
+
useEffect(() => {
|
|
6470
|
+
const el = scrollRef.current;
|
|
6471
|
+
if (!el || typeof ResizeObserver === "undefined") return;
|
|
6472
|
+
const measure = () => setViewport({ w: el.clientWidth, h: el.clientHeight });
|
|
6473
|
+
measure();
|
|
6474
|
+
const ro = new ResizeObserver(measure);
|
|
6475
|
+
ro.observe(el);
|
|
6476
|
+
return () => ro.disconnect();
|
|
6477
|
+
}, []);
|
|
6478
|
+
const bodyH = (viewport.h || (typeof height === "number" ? height : 480)) - headerHeight;
|
|
6479
|
+
const rowStart = virtualize ? Math.max(0, Math.floor(scroll.top / rowHeight) - overscan) : 0;
|
|
6480
|
+
const rowEnd = virtualize ? Math.min(rows.length, Math.ceil((scroll.top + bodyH) / rowHeight) + overscan) : rows.length;
|
|
6481
|
+
let colStart = 0;
|
|
6482
|
+
let colEnd = columns.length;
|
|
6483
|
+
if (virtualize && viewport.w) {
|
|
6484
|
+
const viewLeft = scroll.left;
|
|
6485
|
+
const viewRight = scroll.left + (viewport.w - gutter);
|
|
6486
|
+
colStart = Math.max(0, offsets.findIndex((o, i) => o + widths[i] > viewLeft));
|
|
6487
|
+
if (colStart < 0) colStart = 0;
|
|
6488
|
+
colEnd = offsets.findIndex((o) => o > viewRight);
|
|
6489
|
+
colEnd = colEnd === -1 ? columns.length : Math.min(columns.length, colEnd + 1);
|
|
6490
|
+
colStart = Math.max(0, colStart - overscan);
|
|
6491
|
+
colEnd = Math.min(columns.length, colEnd + overscan);
|
|
6492
|
+
}
|
|
6493
|
+
const visibleRows = Array.from({ length: Math.max(0, rowEnd - rowStart) }, (_, i) => rowStart + i);
|
|
6494
|
+
const visibleCols = Array.from({ length: Math.max(0, colEnd - colStart) }, (_, i) => colStart + i);
|
|
6495
|
+
const commit = useCallback(() => {
|
|
6496
|
+
if (!editing) return;
|
|
6497
|
+
const col = columns[editing.col];
|
|
6498
|
+
onCellEdit?.({ row: editing.row, column: col.key, value: draft });
|
|
6499
|
+
setEditing(null);
|
|
6500
|
+
}, [editing, columns, draft, onCellEdit]);
|
|
6501
|
+
const startEdit = (row, col) => {
|
|
6502
|
+
const c = columns[col];
|
|
6503
|
+
if (!(c.editable ?? editable)) return;
|
|
6504
|
+
setDraft(displayValue(rows[row]?.[c.key] ?? ""));
|
|
6505
|
+
setEditing({ row, col });
|
|
6506
|
+
};
|
|
6507
|
+
return /* @__PURE__ */ jsxs(
|
|
6508
|
+
"div",
|
|
6509
|
+
{
|
|
6510
|
+
ref: scrollRef,
|
|
6511
|
+
onScroll: (e) => setScroll({ top: e.currentTarget.scrollTop, left: e.currentTarget.scrollLeft }),
|
|
6512
|
+
className: cx("relative overflow-auto rounded-lg border border-border bg-surface-raised text-sm", className),
|
|
6513
|
+
style: { height, ...style },
|
|
6514
|
+
role: "grid",
|
|
6515
|
+
"aria-rowcount": rows.length,
|
|
6516
|
+
"aria-colcount": columns.length,
|
|
6517
|
+
children: [
|
|
6518
|
+
/* @__PURE__ */ jsxs("div", { style: { position: "relative", width: gutter + totalWidth, height: headerHeight + totalHeight }, children: [
|
|
6519
|
+
rowNumbers && /* @__PURE__ */ jsx(
|
|
6520
|
+
"div",
|
|
6521
|
+
{
|
|
6522
|
+
className: "border-b border-r border-border bg-surface",
|
|
6523
|
+
style: { position: "absolute", top: scroll.top, left: scroll.left, width: gutter, height: headerHeight, zIndex: 3 }
|
|
6524
|
+
}
|
|
6525
|
+
),
|
|
6526
|
+
visibleCols.map((ci) => {
|
|
6527
|
+
const c = columns[ci];
|
|
6528
|
+
return /* @__PURE__ */ jsx(
|
|
6529
|
+
"div",
|
|
6530
|
+
{
|
|
6531
|
+
role: "columnheader",
|
|
6532
|
+
className: "flex items-center border-b border-r border-border bg-surface px-3 font-medium text-foreground-secondary",
|
|
6533
|
+
style: {
|
|
6534
|
+
position: "absolute",
|
|
6535
|
+
top: scroll.top,
|
|
6536
|
+
left: gutter + offsets[ci],
|
|
6537
|
+
width: widths[ci],
|
|
6538
|
+
height: headerHeight,
|
|
6539
|
+
zIndex: 2,
|
|
6540
|
+
justifyContent: c.align === "right" ? "flex-end" : c.align === "center" ? "center" : "flex-start"
|
|
6541
|
+
},
|
|
6542
|
+
children: /* @__PURE__ */ jsx("span", { className: "truncate", children: c.label ?? c.key })
|
|
6543
|
+
},
|
|
6544
|
+
`h-${c.key}`
|
|
6545
|
+
);
|
|
6546
|
+
}),
|
|
6547
|
+
rowNumbers && visibleRows.map((ri) => /* @__PURE__ */ jsx(
|
|
6548
|
+
"div",
|
|
6549
|
+
{
|
|
6550
|
+
className: "flex items-center justify-center border-b border-r border-border bg-surface text-xs tabular-nums text-foreground-muted",
|
|
6551
|
+
style: { position: "absolute", left: scroll.left, top: headerHeight + ri * rowHeight, width: gutter, height: rowHeight, zIndex: 1 },
|
|
6552
|
+
children: ri + 1
|
|
6553
|
+
},
|
|
6554
|
+
`g-${ri}`
|
|
6555
|
+
)),
|
|
6556
|
+
visibleRows.map(
|
|
6557
|
+
(ri) => visibleCols.map((ci) => {
|
|
6558
|
+
const c = columns[ci];
|
|
6559
|
+
const isEditing = editing?.row === ri && editing?.col === ci;
|
|
6560
|
+
const canEdit = c.editable ?? editable;
|
|
6561
|
+
return /* @__PURE__ */ jsx(
|
|
6562
|
+
"div",
|
|
6563
|
+
{
|
|
6564
|
+
role: "gridcell",
|
|
6565
|
+
onDoubleClick: () => startEdit(ri, ci),
|
|
6566
|
+
className: cx(
|
|
6567
|
+
"flex items-center border-b border-r border-border px-3",
|
|
6568
|
+
ri % 2 ? "bg-surface-raised" : "bg-surface",
|
|
6569
|
+
canEdit && "cursor-text"
|
|
6570
|
+
),
|
|
6571
|
+
style: {
|
|
6572
|
+
position: "absolute",
|
|
6573
|
+
top: headerHeight + ri * rowHeight,
|
|
6574
|
+
left: gutter + offsets[ci],
|
|
6575
|
+
width: widths[ci],
|
|
6576
|
+
height: rowHeight,
|
|
6577
|
+
justifyContent: c.align === "right" ? "flex-end" : c.align === "center" ? "center" : "flex-start"
|
|
6578
|
+
},
|
|
6579
|
+
children: isEditing ? /* @__PURE__ */ jsx(
|
|
6580
|
+
"input",
|
|
6581
|
+
{
|
|
6582
|
+
autoFocus: true,
|
|
6583
|
+
value: draft,
|
|
6584
|
+
onChange: (e) => setDraft(e.target.value),
|
|
6585
|
+
onBlur: commit,
|
|
6586
|
+
onKeyDown: (e) => {
|
|
6587
|
+
if (e.key === "Enter") commit();
|
|
6588
|
+
else if (e.key === "Escape") setEditing(null);
|
|
6589
|
+
},
|
|
6590
|
+
className: "h-full w-full bg-transparent text-foreground outline-none"
|
|
6591
|
+
}
|
|
6592
|
+
) : /* @__PURE__ */ jsx("span", { className: "truncate text-foreground", children: displayValue(rows[ri]?.[c.key] ?? "") })
|
|
6593
|
+
},
|
|
6594
|
+
`${ri}-${c.key}`
|
|
6595
|
+
);
|
|
6596
|
+
})
|
|
6597
|
+
)
|
|
6598
|
+
] }),
|
|
6599
|
+
rows.length === 0 && /* @__PURE__ */ jsx("div", { className: "absolute inset-0 flex items-center justify-center text-foreground-muted", style: { top: headerHeight }, children: emptyState })
|
|
6600
|
+
]
|
|
6601
|
+
}
|
|
6602
|
+
);
|
|
6603
|
+
}
|
|
6604
|
+
function toColumns(cols) {
|
|
6605
|
+
return cols.map((c) => typeof c === "string" ? { key: c, label: c } : { ...c, label: c.label ?? c.key });
|
|
6606
|
+
}
|
|
6607
|
+
function cellValue(v) {
|
|
6608
|
+
if (v != null && typeof v === "object" && "value" in v) return v.value;
|
|
6609
|
+
return v;
|
|
6610
|
+
}
|
|
6611
|
+
function toPlainRows(sheet, columns) {
|
|
6612
|
+
return sheet.rows.map((row) => {
|
|
6613
|
+
const out = {};
|
|
6614
|
+
for (const c of columns) out[c.key] = cellValue(row[c.key]);
|
|
6615
|
+
return out;
|
|
6616
|
+
});
|
|
6617
|
+
}
|
|
6618
|
+
var xlsxPromise = null;
|
|
6619
|
+
var loadXlsx = () => xlsxPromise ??= import('xlsx');
|
|
6620
|
+
var jspdfPromise = null;
|
|
6621
|
+
var loadJspdf = () => jspdfPromise ??= import('jspdf');
|
|
6622
|
+
function coerceToCellType(prev, next) {
|
|
6623
|
+
if (typeof prev === "number") {
|
|
6624
|
+
const n = Number(next);
|
|
6625
|
+
return next.trim() !== "" && Number.isFinite(n) ? n : next;
|
|
6626
|
+
}
|
|
6627
|
+
if (typeof prev === "boolean") {
|
|
6628
|
+
if (/^(true|false)$/i.test(next.trim())) return next.trim().toLowerCase() === "true";
|
|
6629
|
+
return next;
|
|
6630
|
+
}
|
|
6631
|
+
return next;
|
|
6632
|
+
}
|
|
6633
|
+
function Spreadsheet({
|
|
6634
|
+
source,
|
|
6635
|
+
remote,
|
|
6636
|
+
editable = false,
|
|
6637
|
+
onCellEdit,
|
|
6638
|
+
onChange,
|
|
6639
|
+
export: exportFormats = ["xlsx", "csv", "pdf"],
|
|
6640
|
+
fileName,
|
|
6641
|
+
virtualize = true,
|
|
6642
|
+
className = "",
|
|
6643
|
+
style
|
|
6644
|
+
}) {
|
|
6645
|
+
const [sheets, setSheets] = useState(Array.isArray(source) ? source : null);
|
|
6646
|
+
const [active, setActive] = useState(0);
|
|
6647
|
+
const [status, setStatus] = useState(Array.isArray(source) ? "ready" : "loading");
|
|
6648
|
+
const [error, setError] = useState(null);
|
|
6649
|
+
const [reloadKey, setReloadKey] = useState(0);
|
|
6650
|
+
useEffect(() => {
|
|
6651
|
+
if (Array.isArray(source)) {
|
|
6652
|
+
setSheets(source);
|
|
6653
|
+
setStatus("ready");
|
|
6654
|
+
return;
|
|
6655
|
+
}
|
|
6656
|
+
let cancelled = false;
|
|
6657
|
+
setStatus("loading");
|
|
6658
|
+
setError(null);
|
|
6659
|
+
(async () => {
|
|
6660
|
+
try {
|
|
6661
|
+
const bytes = await sourceToBytes(source, remote);
|
|
6662
|
+
const XLSX = await loadXlsx();
|
|
6663
|
+
const wb = XLSX.read(bytes, { type: "array" });
|
|
6664
|
+
const parsed = wb.SheetNames.map((name) => {
|
|
6665
|
+
const ws = wb.Sheets[name];
|
|
6666
|
+
const aoa = XLSX.utils.sheet_to_json(ws, { header: 1, blankrows: false, defval: null });
|
|
6667
|
+
const headerRow = aoa[0] ?? [];
|
|
6668
|
+
const columns2 = headerRow.map((h, i) => ({
|
|
6669
|
+
key: h != null && String(h).length ? String(h) : `col_${i}`,
|
|
6670
|
+
label: h != null && String(h).length ? String(h) : `Column ${i + 1}`
|
|
6671
|
+
}));
|
|
6672
|
+
const rows = aoa.slice(1).map((r) => {
|
|
6673
|
+
const rec = {};
|
|
6674
|
+
columns2.forEach((c, i) => {
|
|
6675
|
+
rec[c.key] = r[i] ?? null;
|
|
6676
|
+
});
|
|
6677
|
+
return rec;
|
|
6678
|
+
});
|
|
6679
|
+
return { name, columns: columns2, rows };
|
|
6680
|
+
});
|
|
6681
|
+
if (cancelled) return;
|
|
6682
|
+
setSheets(parsed);
|
|
6683
|
+
setActive(0);
|
|
6684
|
+
setStatus("ready");
|
|
6685
|
+
} catch (err) {
|
|
6686
|
+
if (cancelled) return;
|
|
6687
|
+
setError(err);
|
|
6688
|
+
setStatus("error");
|
|
6689
|
+
}
|
|
6690
|
+
})();
|
|
6691
|
+
return () => {
|
|
6692
|
+
cancelled = true;
|
|
6693
|
+
};
|
|
6694
|
+
}, [source, remote, reloadKey]);
|
|
6695
|
+
const sheet = sheets?.[active];
|
|
6696
|
+
const columns = useMemo(() => sheet ? toColumns(sheet.columns) : [], [sheet]);
|
|
6697
|
+
const plainRows = useMemo(() => sheet ? toPlainRows(sheet, columns) : [], [sheet, columns]);
|
|
6698
|
+
const handleCellEdit = useCallback(({ row, column, value }) => {
|
|
6699
|
+
let coerced = value;
|
|
6700
|
+
setSheets((prev) => {
|
|
6701
|
+
if (!prev) return prev;
|
|
6702
|
+
const next = prev.map((s, i) => i === active ? { ...s, rows: s.rows.map((r) => ({ ...r })) } : s);
|
|
6703
|
+
const target = next[active];
|
|
6704
|
+
const existing = target.rows[row]?.[column];
|
|
6705
|
+
const prevValue = cellValue(existing);
|
|
6706
|
+
coerced = coerceToCellType(prevValue, value);
|
|
6707
|
+
if (existing != null && typeof existing === "object" && "value" in existing) {
|
|
6708
|
+
target.rows[row][column] = { ...existing, value: coerced };
|
|
6709
|
+
} else {
|
|
6710
|
+
target.rows[row][column] = coerced;
|
|
6711
|
+
}
|
|
6712
|
+
onChange?.(next);
|
|
6713
|
+
return next;
|
|
6714
|
+
});
|
|
6715
|
+
onCellEdit?.({ sheet: sheets?.[active]?.name ?? "", row, column, value: coerced });
|
|
6716
|
+
}, [active, onCellEdit, onChange, sheets]);
|
|
6717
|
+
const baseName = useMemo(
|
|
6718
|
+
() => (fileName || (typeof source === "object" && "name" in source ? sourceName(source) : null) || "spreadsheet").replace(/\.[^.]+$/, ""),
|
|
6719
|
+
[fileName, source]
|
|
6720
|
+
);
|
|
6721
|
+
const sheetAoa = useCallback((s) => {
|
|
6722
|
+
const cols = toColumns(s.columns);
|
|
6723
|
+
const header = cols.map((c) => typeof c.label === "string" ? c.label : c.key);
|
|
6724
|
+
const body = s.rows.map((r) => cols.map((c) => cellValue(r[c.key])));
|
|
6725
|
+
return [header, ...body];
|
|
6726
|
+
}, []);
|
|
6727
|
+
const exportXlsx = useCallback(async () => {
|
|
6728
|
+
if (!sheets) return;
|
|
6729
|
+
const XLSX = await loadXlsx();
|
|
6730
|
+
const wb = XLSX.utils.book_new();
|
|
6731
|
+
sheets.forEach((s) => {
|
|
6732
|
+
const ws = XLSX.utils.aoa_to_sheet(sheetAoa(s));
|
|
6733
|
+
XLSX.utils.book_append_sheet(wb, ws, (s.name || "Sheet").slice(0, 31));
|
|
6734
|
+
});
|
|
6735
|
+
const out = XLSX.write(wb, { bookType: "xlsx", type: "array" });
|
|
6736
|
+
downloadBlob(new Blob([out], { type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" }), `${baseName}.xlsx`);
|
|
6737
|
+
}, [sheets, sheetAoa, baseName]);
|
|
6738
|
+
const exportCsv = useCallback(() => {
|
|
6739
|
+
if (!sheet) return;
|
|
6740
|
+
const aoa = sheetAoa(sheet);
|
|
6741
|
+
const esc = (v) => {
|
|
6742
|
+
const s = v == null ? "" : String(v);
|
|
6743
|
+
return /[",\n]/.test(s) ? `"${s.replace(/"/g, '""')}"` : s;
|
|
6744
|
+
};
|
|
6745
|
+
const csv = aoa.map((row) => row.map(esc).join(",")).join("\r\n");
|
|
6746
|
+
downloadBlob(new Blob([`\uFEFF${csv}`], { type: "text/csv;charset=utf-8" }), `${baseName}.csv`);
|
|
6747
|
+
}, [sheet, sheetAoa, baseName]);
|
|
6748
|
+
const exportPdf = useCallback(async () => {
|
|
6749
|
+
if (!sheet) return;
|
|
6750
|
+
const { jsPDF } = await loadJspdf();
|
|
6751
|
+
const doc = new jsPDF({ orientation: "landscape", unit: "pt", format: "a4" });
|
|
6752
|
+
const aoa = sheetAoa(sheet);
|
|
6753
|
+
const margin2 = 32;
|
|
6754
|
+
const pageW = doc.internal.pageSize.getWidth();
|
|
6755
|
+
const pageH = doc.internal.pageSize.getHeight();
|
|
6756
|
+
const cols = aoa[0]?.length || 1;
|
|
6757
|
+
const colW = (pageW - margin2 * 2) / cols;
|
|
6758
|
+
const rowH = 18;
|
|
6759
|
+
let y = margin2;
|
|
6760
|
+
doc.setFontSize(9);
|
|
6761
|
+
const drawRow = (row, header) => {
|
|
6762
|
+
if (y + rowH > pageH - margin2) {
|
|
6763
|
+
doc.addPage();
|
|
6764
|
+
y = margin2;
|
|
6765
|
+
}
|
|
6766
|
+
doc.setFont("helvetica", header ? "bold" : "normal");
|
|
6767
|
+
row.forEach((cell, i) => {
|
|
6768
|
+
const text = cell == null ? "" : String(cell);
|
|
6769
|
+
doc.text(text.length > 24 ? `${text.slice(0, 23)}\u2026` : text, margin2 + i * colW + 2, y + 12);
|
|
6770
|
+
});
|
|
6771
|
+
doc.setDrawColor(210);
|
|
6772
|
+
doc.line(margin2, y + rowH, pageW - margin2, y + rowH);
|
|
6773
|
+
y += rowH;
|
|
6774
|
+
};
|
|
6775
|
+
doc.setFont("helvetica", "bold");
|
|
6776
|
+
doc.setFontSize(12);
|
|
6777
|
+
doc.text(sheet.name || baseName, margin2, y);
|
|
6778
|
+
y += 22;
|
|
6779
|
+
doc.setFontSize(9);
|
|
6780
|
+
aoa.forEach((row, i) => drawRow(row, i === 0));
|
|
6781
|
+
doc.save(`${baseName}.pdf`);
|
|
6782
|
+
}, [sheet, sheetAoa, baseName]);
|
|
6783
|
+
const runExport = (fmt) => {
|
|
6784
|
+
if (fmt === "xlsx") void exportXlsx();
|
|
6785
|
+
else if (fmt === "csv") exportCsv();
|
|
6786
|
+
else void exportPdf();
|
|
6787
|
+
};
|
|
6788
|
+
if (status === "error") {
|
|
6789
|
+
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: [
|
|
6790
|
+
/* @__PURE__ */ jsx("p", { className: "text-sm font-medium text-status-error", children: "Couldn\u2019t load the spreadsheet" }),
|
|
6791
|
+
error?.message && /* @__PURE__ */ jsx("p", { className: "max-w-md text-xs text-foreground-muted", children: error.message }),
|
|
6792
|
+
/* @__PURE__ */ jsx(Button_default, { content: "Retry", size: "sm", variant: "outline", onClick: () => setReloadKey((k) => k + 1) })
|
|
6793
|
+
] });
|
|
6794
|
+
}
|
|
6795
|
+
if (status === "loading" || !sheets) {
|
|
6796
|
+
return /* @__PURE__ */ jsxs("div", { className: cx("rounded-lg border border-border bg-surface-raised p-4", className), style, children: [
|
|
6797
|
+
/* @__PURE__ */ jsx(SkeletonBox, { height: 32, className: "mb-3 rounded" }),
|
|
6798
|
+
/* @__PURE__ */ jsx(SkeletonBox, { height: 360, className: "rounded" })
|
|
6799
|
+
] });
|
|
6800
|
+
}
|
|
6801
|
+
const formats = exportFormats || [];
|
|
6802
|
+
const exportLabels = {
|
|
6803
|
+
xlsx: "Excel workbook (.xlsx)",
|
|
6804
|
+
csv: "CSV \u2014 this sheet (.csv)",
|
|
6805
|
+
pdf: "PDF table (.pdf)"
|
|
6806
|
+
};
|
|
6807
|
+
return /* @__PURE__ */ jsxs("div", { className: cx("flex flex-col overflow-hidden rounded-lg border border-border bg-surface-raised", className), style, children: [
|
|
6808
|
+
/* @__PURE__ */ jsxs("div", { className: "flex flex-shrink-0 items-center gap-2 border-b border-border bg-surface px-2 py-1.5", children: [
|
|
6809
|
+
sheets.length > 1 ? /* @__PURE__ */ jsx("div", { role: "tablist", "aria-label": "Sheets", className: "flex min-w-0 flex-1 items-center gap-0.5 overflow-x-auto", children: sheets.map((s, i) => /* @__PURE__ */ jsx(
|
|
6810
|
+
"button",
|
|
6811
|
+
{
|
|
6812
|
+
role: "tab",
|
|
6813
|
+
type: "button",
|
|
6814
|
+
"aria-selected": i === active,
|
|
6815
|
+
onClick: () => setActive(i),
|
|
6816
|
+
className: cx(
|
|
6817
|
+
"flex-shrink-0 rounded-md px-3 py-1 text-sm font-medium transition-colors",
|
|
6818
|
+
"focus:outline-none focus-visible:ring-2 focus-visible:ring-accent",
|
|
6819
|
+
i === active ? "bg-accent text-accent-fg shadow-sm" : "text-foreground-secondary hover:bg-surface-raised hover:text-foreground"
|
|
6820
|
+
),
|
|
6821
|
+
children: s.name || `Sheet ${i + 1}`
|
|
6822
|
+
},
|
|
6823
|
+
`${s.name}-${i}`
|
|
6824
|
+
)) }) : /* @__PURE__ */ jsx("span", { className: "flex-1 truncate px-1 text-sm font-medium text-foreground", children: sheet?.name || "Sheet 1" }),
|
|
6825
|
+
/* @__PURE__ */ jsxs("span", { className: "hidden flex-shrink-0 px-1 text-xs tabular-nums text-foreground-muted sm:inline", children: [
|
|
6826
|
+
plainRows.length.toLocaleString(),
|
|
6827
|
+
" ",
|
|
6828
|
+
plainRows.length === 1 ? "row" : "rows",
|
|
6829
|
+
" \xB7 ",
|
|
6830
|
+
columns.length,
|
|
6831
|
+
" cols"
|
|
6832
|
+
] }),
|
|
6833
|
+
formats.length > 0 && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
6834
|
+
/* @__PURE__ */ jsx("span", { className: "h-5 w-px flex-shrink-0 bg-border", "aria-hidden": "true" }),
|
|
6835
|
+
/* @__PURE__ */ jsx(
|
|
6836
|
+
MenuButton,
|
|
6837
|
+
{
|
|
6838
|
+
label: "Export",
|
|
6839
|
+
size: "sm",
|
|
6840
|
+
variant: "outline",
|
|
6841
|
+
align: "end",
|
|
6842
|
+
icon: /* @__PURE__ */ jsx(DownloadIcon2, {}),
|
|
6843
|
+
items: formats.map((fmt) => ({
|
|
6844
|
+
key: fmt,
|
|
6845
|
+
label: exportLabels[fmt],
|
|
6846
|
+
onSelect: () => runExport(fmt)
|
|
6847
|
+
}))
|
|
6848
|
+
}
|
|
6849
|
+
)
|
|
6850
|
+
] })
|
|
6851
|
+
] }),
|
|
6852
|
+
/* @__PURE__ */ jsx(
|
|
6853
|
+
DataGrid,
|
|
6854
|
+
{
|
|
6855
|
+
columns,
|
|
6856
|
+
rows: plainRows,
|
|
6857
|
+
editable,
|
|
6858
|
+
virtualize,
|
|
6859
|
+
onCellEdit: handleCellEdit,
|
|
6860
|
+
className: "!rounded-none !border-0"
|
|
6861
|
+
}
|
|
6862
|
+
)
|
|
6863
|
+
] });
|
|
6864
|
+
}
|
|
6865
|
+
var DownloadIcon2 = () => /* @__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" }) });
|
|
5916
6866
|
function ThemeSwitch({ checked, onChange, label = "Toggle dark mode", className = "" }) {
|
|
5917
6867
|
const id = useId();
|
|
5918
6868
|
return /* @__PURE__ */ jsx("label", { htmlFor: id, className: `flex items-center gap-2 cursor-pointer select-none ${className}`.trim(), children: /* @__PURE__ */ jsx(
|
|
@@ -6013,7 +6963,7 @@ function FlyoutItems({ items }) {
|
|
|
6013
6963
|
}
|
|
6014
6964
|
function CollapsedFlyout({ item, trigger }) {
|
|
6015
6965
|
const [open, setOpen] = useState(false);
|
|
6016
|
-
const closeTimer =
|
|
6966
|
+
const closeTimer = React34.useRef(void 0);
|
|
6017
6967
|
const openNow = () => {
|
|
6018
6968
|
window.clearTimeout(closeTimer.current);
|
|
6019
6969
|
setOpen(true);
|
|
@@ -6022,7 +6972,7 @@ function CollapsedFlyout({ item, trigger }) {
|
|
|
6022
6972
|
window.clearTimeout(closeTimer.current);
|
|
6023
6973
|
closeTimer.current = window.setTimeout(() => setOpen(false), 150);
|
|
6024
6974
|
};
|
|
6025
|
-
|
|
6975
|
+
React34.useEffect(() => () => window.clearTimeout(closeTimer.current), []);
|
|
6026
6976
|
return /* @__PURE__ */ jsxs(DropdownMenu2.Root, { open, onOpenChange: setOpen, modal: false, children: [
|
|
6027
6977
|
/* @__PURE__ */ jsx(
|
|
6028
6978
|
DropdownMenu2.Trigger,
|
|
@@ -6314,8 +7264,8 @@ function MegaMenuLink({ href, icon, description, active, onClick, children, clas
|
|
|
6314
7264
|
function MegaMenuFeatured({ children, className = "" }) {
|
|
6315
7265
|
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
7266
|
}
|
|
6317
|
-
var elementsOfType = (children, type) =>
|
|
6318
|
-
(c) =>
|
|
7267
|
+
var elementsOfType = (children, type) => React34.Children.toArray(children).filter(
|
|
7268
|
+
(c) => React34.isValidElement(c) && c.type === type
|
|
6319
7269
|
);
|
|
6320
7270
|
var MOBILE_CHEVRON = /* @__PURE__ */ jsx(
|
|
6321
7271
|
"svg",
|
|
@@ -6352,9 +7302,9 @@ function MobileLinkRow({ link, onNavigate }) {
|
|
|
6352
7302
|
);
|
|
6353
7303
|
}
|
|
6354
7304
|
function MobilePanel({ panel, onNavigate }) {
|
|
6355
|
-
const nodes =
|
|
7305
|
+
const nodes = React34.Children.toArray(panel.props.children);
|
|
6356
7306
|
return /* @__PURE__ */ jsx("div", { className: "flex flex-col gap-4 px-2 pb-3 pt-1", children: nodes.map((node, i) => {
|
|
6357
|
-
if (!
|
|
7307
|
+
if (!React34.isValidElement(node)) return null;
|
|
6358
7308
|
const el = node;
|
|
6359
7309
|
if (el.type === MegaMenuSection) {
|
|
6360
7310
|
const { title, children } = el.props;
|
|
@@ -6763,7 +7713,7 @@ function ThemeProvider({
|
|
|
6763
7713
|
className = "",
|
|
6764
7714
|
style
|
|
6765
7715
|
}) {
|
|
6766
|
-
const id =
|
|
7716
|
+
const id = React34.useId().replace(/:/g, "");
|
|
6767
7717
|
const scopeClass = `geo-th-${id}`;
|
|
6768
7718
|
const divRef = useRef(null);
|
|
6769
7719
|
useEffect(() => {
|
|
@@ -7488,7 +8438,7 @@ function MultiTagRow({
|
|
|
7488
8438
|
const wrap = wrapRef.current;
|
|
7489
8439
|
const measure = measureRef.current;
|
|
7490
8440
|
if (!wrap || !measure) return;
|
|
7491
|
-
const
|
|
8441
|
+
const GAP2 = 6;
|
|
7492
8442
|
const recompute = () => {
|
|
7493
8443
|
const avail = wrap.clientWidth;
|
|
7494
8444
|
const tagEls = Array.from(measure.querySelectorAll("[data-mt]"));
|
|
@@ -7502,7 +8452,7 @@ function MultiTagRow({
|
|
|
7502
8452
|
let used = 0;
|
|
7503
8453
|
let count = 0;
|
|
7504
8454
|
for (let i = 0; i < widths.length; i++) {
|
|
7505
|
-
const w = widths[i] + (i > 0 ?
|
|
8455
|
+
const w = widths[i] + (i > 0 ? GAP2 : 0);
|
|
7506
8456
|
if (used + w <= avail) {
|
|
7507
8457
|
used += w;
|
|
7508
8458
|
count++;
|
|
@@ -7511,8 +8461,8 @@ function MultiTagRow({
|
|
|
7511
8461
|
if (count < widths.length) {
|
|
7512
8462
|
while (count > 0) {
|
|
7513
8463
|
let t = 0;
|
|
7514
|
-
for (let i = 0; i < count; i++) t += widths[i] + (i > 0 ?
|
|
7515
|
-
t +=
|
|
8464
|
+
for (let i = 0; i < count; i++) t += widths[i] + (i > 0 ? GAP2 : 0);
|
|
8465
|
+
t += GAP2 + moreW;
|
|
7516
8466
|
if (t <= avail) break;
|
|
7517
8467
|
count--;
|
|
7518
8468
|
}
|
|
@@ -8472,7 +9422,7 @@ function DatePicker({
|
|
|
8472
9422
|
setOpen(false);
|
|
8473
9423
|
}
|
|
8474
9424
|
};
|
|
8475
|
-
const
|
|
9425
|
+
const displayValue2 = value ? format(value) : "";
|
|
8476
9426
|
return /* @__PURE__ */ jsxs("div", { className: `flex flex-col gap-1 ${className}`.trim(), children: [
|
|
8477
9427
|
/* @__PURE__ */ jsxs("div", { className: `flex ${layout === "vertical" ? "flex-col gap-1.5" : "flex-row items-start gap-3"}`, children: [
|
|
8478
9428
|
/* @__PURE__ */ jsx(
|
|
@@ -8499,7 +9449,7 @@ function DatePicker({
|
|
|
8499
9449
|
"aria-expanded": open,
|
|
8500
9450
|
className: `flex items-center justify-between cursor-pointer select-none ${!style?.width ? "min-w-[200px]" : ""} ${fieldShell({ size, hasError, disabled })}`,
|
|
8501
9451
|
children: [
|
|
8502
|
-
/* @__PURE__ */ jsx("span", { className: `text-sm truncate ${
|
|
9452
|
+
/* @__PURE__ */ jsx("span", { className: `text-sm truncate ${displayValue2 ? "" : "text-foreground-muted"}`, children: displayValue2 || placeholder }),
|
|
8503
9453
|
/* @__PURE__ */ jsx(CalendarIcon, {})
|
|
8504
9454
|
]
|
|
8505
9455
|
}
|
|
@@ -9071,7 +10021,7 @@ function OtpInput({
|
|
|
9071
10021
|
emit(valid.join(""));
|
|
9072
10022
|
focusBox(valid.length);
|
|
9073
10023
|
};
|
|
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(
|
|
10024
|
+
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
10025
|
/* @__PURE__ */ jsx(
|
|
9076
10026
|
"input",
|
|
9077
10027
|
{
|
|
@@ -10130,7 +11080,7 @@ function Blog({
|
|
|
10130
11080
|
post.tag != null && !post.image && /* @__PURE__ */ jsx("div", { children: /* @__PURE__ */ jsx(Badge, { tone: "accent", variant: "soft", size: "sm", children: post.tag }) }),
|
|
10131
11081
|
/* @__PURE__ */ jsx("h3", { className: "text-base font-semibold leading-snug text-foreground transition-colors group-hover:text-accent", children: post.title }),
|
|
10132
11082
|
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(
|
|
11083
|
+
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
11084
|
j > 0 && /* @__PURE__ */ jsx("span", { "aria-hidden": "true", children: "\xB7" }),
|
|
10135
11085
|
/* @__PURE__ */ jsx("span", { children: m })
|
|
10136
11086
|
] }, j)) })
|
|
@@ -10303,6 +11253,6 @@ function LeadCapture({
|
|
|
10303
11253
|
);
|
|
10304
11254
|
}
|
|
10305
11255
|
|
|
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 };
|
|
11256
|
+
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
11257
|
//# sourceMappingURL=index.js.map
|
|
10308
11258
|
//# sourceMappingURL=index.js.map
|