@geomak/ui 7.8.0 → 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 +296 -109
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +12 -2
- package/dist/index.d.ts +12 -2
- package/dist/index.js +296 -109
- package/dist/index.js.map +1 -1
- package/dist/styles.css +16 -0
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -5973,6 +5973,15 @@ async function sourceToBytes(source, remote) {
|
|
|
5973
5973
|
}
|
|
5974
5974
|
throw new Error("Unsupported source type");
|
|
5975
5975
|
}
|
|
5976
|
+
function downloadBlob(blob, name) {
|
|
5977
|
+
if (typeof document === "undefined") return;
|
|
5978
|
+
const url = URL.createObjectURL(blob);
|
|
5979
|
+
const a = document.createElement("a");
|
|
5980
|
+
a.href = url;
|
|
5981
|
+
a.download = name;
|
|
5982
|
+
a.click();
|
|
5983
|
+
setTimeout(() => URL.revokeObjectURL(url), 1e3);
|
|
5984
|
+
}
|
|
5976
5985
|
function sourceName(source) {
|
|
5977
5986
|
if (typeof File !== "undefined" && source instanceof File) return source.name;
|
|
5978
5987
|
if (isUrlSource(source)) {
|
|
@@ -5983,17 +5992,45 @@ function sourceName(source) {
|
|
|
5983
5992
|
return void 0;
|
|
5984
5993
|
}
|
|
5985
5994
|
var pdfjsPromise = null;
|
|
5986
|
-
function loadPdfjs() {
|
|
5995
|
+
function loadPdfjs(workerSrc) {
|
|
5987
5996
|
if (pdfjsPromise) return pdfjsPromise;
|
|
5988
5997
|
pdfjsPromise = import('pdfjs-dist').then((pdfjs) => {
|
|
5989
5998
|
if (!pdfjs.GlobalWorkerOptions.workerSrc) {
|
|
5990
|
-
pdfjs.GlobalWorkerOptions.workerSrc = `https://cdn.jsdelivr.net/npm/pdfjs-dist@${pdfjs.version}/build/pdf.worker.min.mjs`;
|
|
5999
|
+
pdfjs.GlobalWorkerOptions.workerSrc = workerSrc ?? `https://cdn.jsdelivr.net/npm/pdfjs-dist@${pdfjs.version}/build/pdf.worker.min.mjs`;
|
|
5991
6000
|
}
|
|
5992
6001
|
return pdfjs;
|
|
5993
6002
|
});
|
|
5994
6003
|
return pdfjsPromise;
|
|
5995
6004
|
}
|
|
5996
6005
|
var GAP = 12;
|
|
6006
|
+
var SEARCH_DEBOUNCE = 220;
|
|
6007
|
+
var TEXT_LAYER_STYLE_ID = "oxygen-pdf-textlayer";
|
|
6008
|
+
var TEXT_LAYER_CSS = `
|
|
6009
|
+
.oxy-textLayer{position:absolute;inset:0;overflow:clip;line-height:1;text-size-adjust:none;forced-color-adjust:none;transform-origin:0 0}
|
|
6010
|
+
.oxy-textLayer span,.oxy-textLayer br{color:transparent;position:absolute;white-space:pre;cursor:text;transform-origin:0 0}
|
|
6011
|
+
.oxy-textLayer span.markedContent{top:0;height:0}
|
|
6012
|
+
.oxy-textLayer ::selection{background:color-mix(in srgb, var(--color-accent) 35%, transparent)}
|
|
6013
|
+
`;
|
|
6014
|
+
function ensureTextLayerStyles() {
|
|
6015
|
+
if (typeof document === "undefined" || document.getElementById(TEXT_LAYER_STYLE_ID)) return;
|
|
6016
|
+
const el = document.createElement("style");
|
|
6017
|
+
el.id = TEXT_LAYER_STYLE_ID;
|
|
6018
|
+
el.textContent = TEXT_LAYER_CSS;
|
|
6019
|
+
document.head.appendChild(el);
|
|
6020
|
+
}
|
|
6021
|
+
function pageIndexAt(offsets, y) {
|
|
6022
|
+
let lo = 0;
|
|
6023
|
+
let hi = offsets.length - 1;
|
|
6024
|
+
let ans = 0;
|
|
6025
|
+
while (lo <= hi) {
|
|
6026
|
+
const mid = lo + hi >> 1;
|
|
6027
|
+
if (offsets[mid] <= y) {
|
|
6028
|
+
ans = mid;
|
|
6029
|
+
lo = mid + 1;
|
|
6030
|
+
} else hi = mid - 1;
|
|
6031
|
+
}
|
|
6032
|
+
return ans;
|
|
6033
|
+
}
|
|
5997
6034
|
function PdfViewer({
|
|
5998
6035
|
source,
|
|
5999
6036
|
remote,
|
|
@@ -6002,6 +6039,7 @@ function PdfViewer({
|
|
|
6002
6039
|
toolbar = true,
|
|
6003
6040
|
thumbnails = false,
|
|
6004
6041
|
textLayer = true,
|
|
6042
|
+
workerSrc,
|
|
6005
6043
|
onLoad,
|
|
6006
6044
|
onError,
|
|
6007
6045
|
onPageChange,
|
|
@@ -6012,6 +6050,7 @@ function PdfViewer({
|
|
|
6012
6050
|
const [doc, setDoc] = React34.useState(null);
|
|
6013
6051
|
const [numPages, setNumPages] = React34.useState(0);
|
|
6014
6052
|
const [baseSize, setBaseSize] = React34.useState(null);
|
|
6053
|
+
const [sizes, setSizes] = React34.useState([]);
|
|
6015
6054
|
const [status, setStatus] = React34.useState("loading");
|
|
6016
6055
|
const [error, setError] = React34.useState(null);
|
|
6017
6056
|
const [reloadKey, setReloadKey] = React34.useState(0);
|
|
@@ -6024,6 +6063,13 @@ function PdfViewer({
|
|
|
6024
6063
|
const [query, setQuery] = React34.useState("");
|
|
6025
6064
|
const [matchPages, setMatchPages] = React34.useState(null);
|
|
6026
6065
|
const [matchIdx, setMatchIdx] = React34.useState(0);
|
|
6066
|
+
const [searching, setSearching] = React34.useState(false);
|
|
6067
|
+
const [pageDraft, setPageDraft] = React34.useState("");
|
|
6068
|
+
const pageEditing = React34.useRef(false);
|
|
6069
|
+
const pageText = React34.useRef(/* @__PURE__ */ new Map());
|
|
6070
|
+
const searchSeq = React34.useRef(0);
|
|
6071
|
+
const searchTimer = React34.useRef(null);
|
|
6072
|
+
const measured = React34.useRef(/* @__PURE__ */ new Set());
|
|
6027
6073
|
const tb = toolbar === true ? { zoom: true, pager: true, download: true, print: true, search: true } : toolbar || {};
|
|
6028
6074
|
React34.useEffect(() => {
|
|
6029
6075
|
let cancelled = false;
|
|
@@ -6032,7 +6078,12 @@ function PdfViewer({
|
|
|
6032
6078
|
setError(null);
|
|
6033
6079
|
setDoc(null);
|
|
6034
6080
|
setBaseSize(null);
|
|
6035
|
-
|
|
6081
|
+
setSizes([]);
|
|
6082
|
+
pageText.current.clear();
|
|
6083
|
+
measured.current = /* @__PURE__ */ new Set();
|
|
6084
|
+
setMatchPages(null);
|
|
6085
|
+
setMatchIdx(0);
|
|
6086
|
+
loadPdfjs(workerSrc).then(async (pdfjs2) => {
|
|
6036
6087
|
if (cancelled) return;
|
|
6037
6088
|
setPdfjs(pdfjs2);
|
|
6038
6089
|
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()) };
|
|
@@ -6045,9 +6096,12 @@ function PdfViewer({
|
|
|
6045
6096
|
const first = await pdf.getPage(1);
|
|
6046
6097
|
const vp = first.getViewport({ scale: 1 });
|
|
6047
6098
|
if (cancelled) return;
|
|
6099
|
+
const base = { width: vp.width, height: vp.height };
|
|
6048
6100
|
setDoc(pdf);
|
|
6049
6101
|
setNumPages(pdf.numPages);
|
|
6050
|
-
setBaseSize(
|
|
6102
|
+
setBaseSize(base);
|
|
6103
|
+
setSizes(Array.from({ length: pdf.numPages }, () => base));
|
|
6104
|
+
measured.current.add(1);
|
|
6051
6105
|
setStatus("ready");
|
|
6052
6106
|
onLoad?.({ numPages: pdf.numPages });
|
|
6053
6107
|
}).catch((err) => {
|
|
@@ -6060,7 +6114,7 @@ function PdfViewer({
|
|
|
6060
6114
|
cancelled = true;
|
|
6061
6115
|
task?.destroy?.();
|
|
6062
6116
|
};
|
|
6063
|
-
}, [source, remote, reloadKey]);
|
|
6117
|
+
}, [source, remote, reloadKey, workerSrc]);
|
|
6064
6118
|
React34.useEffect(() => () => {
|
|
6065
6119
|
doc?.destroy?.();
|
|
6066
6120
|
}, [doc]);
|
|
@@ -6073,59 +6127,101 @@ function PdfViewer({
|
|
|
6073
6127
|
ro.observe(el);
|
|
6074
6128
|
return () => ro.disconnect();
|
|
6075
6129
|
}, [status]);
|
|
6130
|
+
const handleMeasure = React34.useCallback((p, size) => {
|
|
6131
|
+
setSizes((prev) => {
|
|
6132
|
+
const cur = prev[p - 1];
|
|
6133
|
+
if (cur && Math.abs(cur.width - size.width) < 0.5 && Math.abs(cur.height - size.height) < 0.5) return prev;
|
|
6134
|
+
const next = prev.slice();
|
|
6135
|
+
next[p - 1] = size;
|
|
6136
|
+
return next;
|
|
6137
|
+
});
|
|
6138
|
+
}, []);
|
|
6076
6139
|
const scale = React34.useMemo(() => {
|
|
6077
6140
|
if (!baseSize) return 1;
|
|
6078
6141
|
if (typeof zoomMode === "number") return zoomMode;
|
|
6079
|
-
const avail = Math.max(
|
|
6142
|
+
const avail = Math.max(1, (viewport.w || baseSize.width) - 32);
|
|
6080
6143
|
if (zoomMode === "page-width" || zoomMode === "auto") return avail / baseSize.width;
|
|
6081
6144
|
if (zoomMode === "page-fit") {
|
|
6082
|
-
const availH = Math.max(
|
|
6145
|
+
const availH = Math.max(1, (viewport.h || baseSize.height) - 32);
|
|
6083
6146
|
return Math.min(avail / baseSize.width, availH / baseSize.height);
|
|
6084
6147
|
}
|
|
6085
6148
|
return 1;
|
|
6086
6149
|
}, [zoomMode, baseSize, viewport]);
|
|
6087
|
-
const
|
|
6088
|
-
|
|
6089
|
-
|
|
6150
|
+
const { offsets, total } = React34.useMemo(() => {
|
|
6151
|
+
const offs = new Array(sizes.length);
|
|
6152
|
+
let acc = 0;
|
|
6153
|
+
for (let i = 0; i < sizes.length; i++) {
|
|
6154
|
+
offs[i] = acc;
|
|
6155
|
+
acc += sizes[i].height * scale + GAP;
|
|
6156
|
+
}
|
|
6157
|
+
return { offsets: offs, total: acc };
|
|
6158
|
+
}, [sizes, scale]);
|
|
6090
6159
|
const overscan = 1;
|
|
6091
|
-
const
|
|
6092
|
-
const
|
|
6160
|
+
const hasLayout = offsets.length > 0 && total > 0;
|
|
6161
|
+
const startIdx = hasLayout ? Math.max(0, pageIndexAt(offsets, scrollTop) - overscan) : 0;
|
|
6162
|
+
const endIdx = hasLayout ? Math.min(numPages, pageIndexAt(offsets, scrollTop + viewport.h) + 1 + overscan) : 0;
|
|
6093
6163
|
const visiblePages = Array.from({ length: Math.max(0, endIdx - startIdx) }, (_, i) => startIdx + i + 1);
|
|
6164
|
+
const scrollToPage = React34.useCallback((p) => {
|
|
6165
|
+
const el = scrollRef.current;
|
|
6166
|
+
if (!el || !offsets.length) return;
|
|
6167
|
+
const clamped = Math.min(offsets.length, Math.max(1, p));
|
|
6168
|
+
el.scrollTo({ top: offsets[clamped - 1] ?? 0, behavior: "smooth" });
|
|
6169
|
+
}, [offsets]);
|
|
6094
6170
|
React34.useEffect(() => {
|
|
6095
|
-
if (!
|
|
6096
|
-
const cur =
|
|
6171
|
+
if (!hasLayout) return;
|
|
6172
|
+
const cur = pageIndexAt(offsets, scrollTop + viewport.h / 2) + 1;
|
|
6097
6173
|
if (cur !== page) {
|
|
6098
6174
|
setPage(cur);
|
|
6099
6175
|
onPageChange?.(cur);
|
|
6100
6176
|
}
|
|
6101
|
-
}, [scrollTop,
|
|
6102
|
-
const scrollToPage = React34.useCallback((p) => {
|
|
6103
|
-
const el = scrollRef.current;
|
|
6104
|
-
if (!el || !pageH) return;
|
|
6105
|
-
el.scrollTo({ top: (p - 1) * pageH, behavior: "smooth" });
|
|
6106
|
-
}, [pageH]);
|
|
6177
|
+
}, [scrollTop, offsets, viewport.h, numPages]);
|
|
6107
6178
|
React34.useEffect(() => {
|
|
6108
6179
|
if (status === "ready" && initialPage > 1) scrollToPage(initialPage);
|
|
6109
|
-
}, [status]);
|
|
6180
|
+
}, [status, initialPage]);
|
|
6110
6181
|
const runSearch = React34.useCallback(async (q) => {
|
|
6111
|
-
|
|
6112
|
-
if (!doc || !
|
|
6182
|
+
const needle = q.trim().toLowerCase();
|
|
6183
|
+
if (!doc || !needle) {
|
|
6113
6184
|
setMatchPages(null);
|
|
6114
6185
|
setMatchIdx(0);
|
|
6186
|
+
setSearching(false);
|
|
6115
6187
|
return;
|
|
6116
6188
|
}
|
|
6117
|
-
const
|
|
6189
|
+
const seq = ++searchSeq.current;
|
|
6190
|
+
setSearching(true);
|
|
6118
6191
|
const hits = [];
|
|
6119
6192
|
for (let p = 1; p <= numPages; p++) {
|
|
6120
|
-
|
|
6121
|
-
|
|
6122
|
-
|
|
6193
|
+
let text = pageText.current.get(p);
|
|
6194
|
+
if (text === void 0) {
|
|
6195
|
+
const pg = await doc.getPage(p);
|
|
6196
|
+
const tc = await pg.getTextContent();
|
|
6197
|
+
const built = tc.items.map((it) => "str" in it ? it.str : "").join(" ").toLowerCase();
|
|
6198
|
+
pageText.current.set(p, built);
|
|
6199
|
+
text = built;
|
|
6200
|
+
}
|
|
6201
|
+
if (seq !== searchSeq.current) return;
|
|
6123
6202
|
if (text.includes(needle)) hits.push(p);
|
|
6124
6203
|
}
|
|
6204
|
+
if (seq !== searchSeq.current) return;
|
|
6125
6205
|
setMatchPages(hits);
|
|
6126
6206
|
setMatchIdx(0);
|
|
6207
|
+
setSearching(false);
|
|
6127
6208
|
if (hits.length) scrollToPage(hits[0]);
|
|
6128
6209
|
}, [doc, numPages, scrollToPage]);
|
|
6210
|
+
const onQueryChange = React34.useCallback((v) => {
|
|
6211
|
+
setQuery(v);
|
|
6212
|
+
if (searchTimer.current) clearTimeout(searchTimer.current);
|
|
6213
|
+
if (!v.trim()) {
|
|
6214
|
+
searchSeq.current++;
|
|
6215
|
+
setMatchPages(null);
|
|
6216
|
+
setMatchIdx(0);
|
|
6217
|
+
setSearching(false);
|
|
6218
|
+
return;
|
|
6219
|
+
}
|
|
6220
|
+
searchTimer.current = setTimeout(() => runSearch(v), SEARCH_DEBOUNCE);
|
|
6221
|
+
}, [runSearch]);
|
|
6222
|
+
React34.useEffect(() => () => {
|
|
6223
|
+
if (searchTimer.current) clearTimeout(searchTimer.current);
|
|
6224
|
+
}, []);
|
|
6129
6225
|
const gotoMatch = (dir) => {
|
|
6130
6226
|
if (!matchPages?.length) return;
|
|
6131
6227
|
const next = (matchIdx + dir + matchPages.length) % matchPages.length;
|
|
@@ -6139,15 +6235,9 @@ function PdfViewer({
|
|
|
6139
6235
|
if (typeof Blob !== "undefined" && source instanceof Blob) return new Uint8Array(await source.arrayBuffer());
|
|
6140
6236
|
throw new Error("No bytes available");
|
|
6141
6237
|
}, [doc, source]);
|
|
6142
|
-
const
|
|
6238
|
+
const download = React34.useCallback(async () => {
|
|
6143
6239
|
const bytes = await getBytes();
|
|
6144
|
-
|
|
6145
|
-
const url = URL.createObjectURL(blob);
|
|
6146
|
-
const a = document.createElement("a");
|
|
6147
|
-
a.href = url;
|
|
6148
|
-
a.download = sourceName(source) || "document.pdf";
|
|
6149
|
-
a.click();
|
|
6150
|
-
setTimeout(() => URL.revokeObjectURL(url), 1e3);
|
|
6240
|
+
downloadBlob(new Blob([bytes], { type: "application/pdf" }), sourceName(source) || "document.pdf");
|
|
6151
6241
|
}, [getBytes, source]);
|
|
6152
6242
|
const print = React34.useCallback(async () => {
|
|
6153
6243
|
const bytes = await getBytes();
|
|
@@ -6173,6 +6263,12 @@ function PdfViewer({
|
|
|
6173
6263
|
const cur = typeof zoomMode === "number" ? zoomMode : scale;
|
|
6174
6264
|
setZoomMode(Math.min(5, Math.max(0.25, +(cur * factor).toFixed(2))));
|
|
6175
6265
|
};
|
|
6266
|
+
const zoomPct = Math.round(scale * 100);
|
|
6267
|
+
const commitPageDraft = () => {
|
|
6268
|
+
pageEditing.current = false;
|
|
6269
|
+
const n = parseInt(pageDraft, 10);
|
|
6270
|
+
if (Number.isFinite(n)) scrollToPage(n);
|
|
6271
|
+
};
|
|
6176
6272
|
if (status === "error") {
|
|
6177
6273
|
return /* @__PURE__ */ jsxRuntime.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: [
|
|
6178
6274
|
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm font-medium text-status-error", children: "Couldn\u2019t load the PDF" }),
|
|
@@ -6180,80 +6276,127 @@ function PdfViewer({
|
|
|
6180
6276
|
/* @__PURE__ */ jsxRuntime.jsx(Button_default, { content: "Retry", size: "sm", variant: "outline", onClick: () => setReloadKey((k) => k + 1) })
|
|
6181
6277
|
] });
|
|
6182
6278
|
}
|
|
6279
|
+
const ready = status === "ready";
|
|
6183
6280
|
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: cx("flex flex-col overflow-hidden rounded-lg border border-border bg-surface-raised", className), style: { height: 600, ...style }, children: [
|
|
6184
|
-
toolbar !== false && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-shrink-0 flex-
|
|
6185
|
-
|
|
6186
|
-
/* @__PURE__ */ jsxRuntime.
|
|
6187
|
-
|
|
6188
|
-
|
|
6189
|
-
|
|
6190
|
-
|
|
6281
|
+
toolbar !== false && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-shrink-0 flex-col border-b border-border bg-surface", children: [
|
|
6282
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1 px-2 py-1.5", children: [
|
|
6283
|
+
tb.pager && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1", children: [
|
|
6284
|
+
/* @__PURE__ */ jsxRuntime.jsx(IconButton_default, { type: "ghost", size: "sm", title: "Previous page", onClick: () => scrollToPage(page - 1), icon: /* @__PURE__ */ jsxRuntime.jsx(Chevron5, { dir: "up" }), disabled: !ready || page <= 1 }),
|
|
6285
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1 text-xs text-foreground-secondary select-none", children: [
|
|
6286
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
6287
|
+
"input",
|
|
6288
|
+
{
|
|
6289
|
+
value: pageEditing.current ? pageDraft : ready ? String(page) : "\u2013",
|
|
6290
|
+
onFocus: () => {
|
|
6291
|
+
pageEditing.current = true;
|
|
6292
|
+
setPageDraft(String(page));
|
|
6293
|
+
},
|
|
6294
|
+
onChange: (e) => setPageDraft(e.target.value.replace(/[^\d]/g, "")),
|
|
6295
|
+
onBlur: commitPageDraft,
|
|
6296
|
+
onKeyDown: (e) => {
|
|
6297
|
+
if (e.key === "Enter") e.target.blur();
|
|
6298
|
+
},
|
|
6299
|
+
disabled: !ready,
|
|
6300
|
+
"aria-label": "Page number",
|
|
6301
|
+
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"
|
|
6302
|
+
}
|
|
6303
|
+
),
|
|
6304
|
+
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: "tabular-nums", children: [
|
|
6305
|
+
"/ ",
|
|
6306
|
+
numPages || "\u2013"
|
|
6307
|
+
] })
|
|
6308
|
+
] }),
|
|
6309
|
+
/* @__PURE__ */ jsxRuntime.jsx(IconButton_default, { type: "ghost", size: "sm", title: "Next page", onClick: () => scrollToPage(page + 1), icon: /* @__PURE__ */ jsxRuntime.jsx(Chevron5, { dir: "down" }), disabled: !ready || page >= numPages })
|
|
6191
6310
|
] }),
|
|
6192
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
6193
|
-
|
|
6194
|
-
|
|
6195
|
-
|
|
6196
|
-
|
|
6197
|
-
|
|
6198
|
-
|
|
6199
|
-
|
|
6200
|
-
|
|
6201
|
-
|
|
6202
|
-
|
|
6203
|
-
|
|
6311
|
+
tb.pager && tb.zoom && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "mx-1 h-5 w-px bg-border", "aria-hidden": "true" }),
|
|
6312
|
+
tb.zoom && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1", children: [
|
|
6313
|
+
/* @__PURE__ */ jsxRuntime.jsx(IconButton_default, { type: "ghost", size: "sm", title: "Zoom out", onClick: () => setZoomNum(1 / 1.2), icon: /* @__PURE__ */ jsxRuntime.jsx(MinusIcon, {}), disabled: !ready }),
|
|
6314
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "w-10 text-center text-xs tabular-nums text-foreground-secondary select-none", children: ready ? `${zoomPct}%` : "\u2013" }),
|
|
6315
|
+
/* @__PURE__ */ jsxRuntime.jsx(IconButton_default, { type: "ghost", size: "sm", title: "Zoom in", onClick: () => setZoomNum(1.2), icon: /* @__PURE__ */ jsxRuntime.jsx(PlusIcon, {}), disabled: !ready }),
|
|
6316
|
+
/* @__PURE__ */ jsxRuntime.jsx(IconButton_default, { type: "ghost", size: "sm", title: "Fit width", onClick: () => setZoomMode("page-width"), icon: /* @__PURE__ */ jsxRuntime.jsx(FitWidthIcon, {}), disabled: !ready }),
|
|
6317
|
+
/* @__PURE__ */ jsxRuntime.jsx(IconButton_default, { type: "ghost", size: "sm", title: "Fit page", onClick: () => setZoomMode("page-fit"), icon: /* @__PURE__ */ jsxRuntime.jsx(FitPageIcon, {}), disabled: !ready })
|
|
6318
|
+
] }),
|
|
6319
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "ml-auto flex items-center gap-1", children: [
|
|
6320
|
+
tb.search && /* @__PURE__ */ jsxRuntime.jsx(IconButton_default, { type: showSearch ? "bordered" : "ghost", size: "sm", title: "Search", onClick: () => setShowSearch((s) => !s), icon: /* @__PURE__ */ jsxRuntime.jsx(SearchIcon2, {}), disabled: !ready }),
|
|
6321
|
+
tb.search && (tb.download || tb.print) && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "mx-1 h-5 w-px bg-border", "aria-hidden": "true" }),
|
|
6322
|
+
tb.download && /* @__PURE__ */ jsxRuntime.jsx(IconButton_default, { type: "ghost", size: "sm", title: "Download", onClick: download, icon: /* @__PURE__ */ jsxRuntime.jsx(DownloadIcon, {}), disabled: !ready }),
|
|
6323
|
+
tb.print && /* @__PURE__ */ jsxRuntime.jsx(IconButton_default, { type: "ghost", size: "sm", title: "Print", onClick: print, icon: /* @__PURE__ */ jsxRuntime.jsx(PrintIcon, {}), disabled: !ready })
|
|
6324
|
+
] })
|
|
6204
6325
|
] }),
|
|
6205
|
-
tb.search && showSearch && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex
|
|
6326
|
+
tb.search && showSearch && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 border-t border-border px-2 py-1.5", children: [
|
|
6327
|
+
/* @__PURE__ */ jsxRuntime.jsx(SearchIcon2, {}),
|
|
6206
6328
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
6207
6329
|
"input",
|
|
6208
6330
|
{
|
|
6209
6331
|
autoFocus: true,
|
|
6210
6332
|
value: query,
|
|
6211
|
-
onChange: (e) =>
|
|
6333
|
+
onChange: (e) => onQueryChange(e.target.value),
|
|
6334
|
+
onKeyDown: (e) => {
|
|
6335
|
+
if (e.key === "Enter") gotoMatch(e.shiftKey ? -1 : 1);
|
|
6336
|
+
},
|
|
6212
6337
|
placeholder: "Find in document\u2026",
|
|
6213
|
-
className: "h-7 flex-1
|
|
6338
|
+
className: "h-7 flex-1 bg-transparent text-sm text-foreground outline-none placeholder:text-foreground-muted"
|
|
6214
6339
|
}
|
|
6215
6340
|
),
|
|
6216
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs tabular-nums text-foreground-muted", children: matchPages == null ? "" : matchPages.length ? `${matchIdx + 1}
|
|
6341
|
+
/* @__PURE__ */ jsxRuntime.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" }),
|
|
6217
6342
|
/* @__PURE__ */ jsxRuntime.jsx(IconButton_default, { type: "ghost", size: "sm", title: "Previous match", onClick: () => gotoMatch(-1), icon: /* @__PURE__ */ jsxRuntime.jsx(Chevron5, { dir: "up" }), disabled: !matchPages?.length }),
|
|
6218
|
-
/* @__PURE__ */ jsxRuntime.jsx(IconButton_default, { type: "ghost", size: "sm", title: "Next match", onClick: () => gotoMatch(1), icon: /* @__PURE__ */ jsxRuntime.jsx(Chevron5, { dir: "down" }), disabled: !matchPages?.length })
|
|
6343
|
+
/* @__PURE__ */ jsxRuntime.jsx(IconButton_default, { type: "ghost", size: "sm", title: "Next match", onClick: () => gotoMatch(1), icon: /* @__PURE__ */ jsxRuntime.jsx(Chevron5, { dir: "down" }), disabled: !matchPages?.length }),
|
|
6344
|
+
/* @__PURE__ */ jsxRuntime.jsx(IconButton_default, { type: "ghost", size: "sm", title: "Close search", onClick: () => {
|
|
6345
|
+
setShowSearch(false);
|
|
6346
|
+
onQueryChange("");
|
|
6347
|
+
}, icon: /* @__PURE__ */ jsxRuntime.jsx(CloseIcon, {}) })
|
|
6219
6348
|
] })
|
|
6220
6349
|
] }),
|
|
6221
6350
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex min-h-0 flex-1", children: [
|
|
6222
|
-
thumbnails &&
|
|
6223
|
-
|
|
6224
|
-
|
|
6225
|
-
|
|
6226
|
-
|
|
6227
|
-
|
|
6228
|
-
|
|
6229
|
-
|
|
6230
|
-
|
|
6231
|
-
|
|
6232
|
-
|
|
6233
|
-
|
|
6234
|
-
|
|
6235
|
-
|
|
6236
|
-
|
|
6351
|
+
thumbnails && ready && doc && baseSize && /* @__PURE__ */ jsxRuntime.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) => {
|
|
6352
|
+
const s = sizes[p - 1] ?? baseSize;
|
|
6353
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
6354
|
+
"button",
|
|
6355
|
+
{
|
|
6356
|
+
type: "button",
|
|
6357
|
+
onClick: () => scrollToPage(p),
|
|
6358
|
+
className: cx(
|
|
6359
|
+
"mb-2 block w-full overflow-hidden rounded border bg-surface-raised transition-colors",
|
|
6360
|
+
p === page ? "border-accent ring-1 ring-accent" : "border-border hover:border-border-strong"
|
|
6361
|
+
),
|
|
6362
|
+
style: { aspectRatio: `${s.width} / ${s.height}` },
|
|
6363
|
+
"aria-label": `Page ${p}`,
|
|
6364
|
+
"aria-current": p === page,
|
|
6365
|
+
children: Math.abs(p - page) <= 8 ? /* @__PURE__ */ jsxRuntime.jsx(PdfPage, { pdfjs, doc, page: p, scale: 112 / s.width, textLayer: false }) : /* @__PURE__ */ jsxRuntime.jsx("span", { className: "block py-4 text-center text-xs text-foreground-muted", children: p })
|
|
6366
|
+
},
|
|
6367
|
+
p
|
|
6368
|
+
);
|
|
6369
|
+
}) }),
|
|
6237
6370
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
6238
6371
|
"div",
|
|
6239
6372
|
{
|
|
6240
6373
|
ref: scrollRef,
|
|
6241
6374
|
onScroll: (e) => setScrollTop(e.currentTarget.scrollTop),
|
|
6242
6375
|
className: "relative flex-1 overflow-auto bg-background",
|
|
6243
|
-
children:
|
|
6244
|
-
|
|
6245
|
-
|
|
6246
|
-
|
|
6247
|
-
|
|
6248
|
-
|
|
6249
|
-
|
|
6250
|
-
|
|
6376
|
+
children: !ready || !baseSize ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-col items-center gap-3 p-6", children: /* @__PURE__ */ jsxRuntime.jsx(SkeletonBox, { width: Math.min((viewport.w || 528) - 48, 560), height: 680, className: "rounded" }) }) : numPages === 0 ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex h-full items-center justify-center text-sm text-foreground-muted", children: "Empty document" }) : /* @__PURE__ */ jsxRuntime.jsx("div", { style: { height: total, position: "relative" }, children: visiblePages.map((p) => {
|
|
6377
|
+
const s = sizes[p - 1] ?? baseSize;
|
|
6378
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
6379
|
+
"div",
|
|
6380
|
+
{
|
|
6381
|
+
style: { position: "absolute", top: offsets[p - 1], left: 0, right: 0, display: "flex", justifyContent: "center", paddingTop: GAP / 2 },
|
|
6382
|
+
children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "relative shadow-md", style: { width: s.width * scale, height: s.height * scale }, children: /* @__PURE__ */ jsxRuntime.jsx(PdfPage, { pdfjs, doc, page: p, scale, textLayer, onMeasure: handleMeasure }) })
|
|
6383
|
+
},
|
|
6384
|
+
p
|
|
6385
|
+
);
|
|
6386
|
+
}) })
|
|
6251
6387
|
}
|
|
6252
6388
|
)
|
|
6253
6389
|
] })
|
|
6254
6390
|
] });
|
|
6255
6391
|
}
|
|
6256
|
-
function PdfPage({
|
|
6392
|
+
function PdfPage({
|
|
6393
|
+
pdfjs,
|
|
6394
|
+
doc,
|
|
6395
|
+
page,
|
|
6396
|
+
scale,
|
|
6397
|
+
textLayer,
|
|
6398
|
+
onMeasure
|
|
6399
|
+
}) {
|
|
6257
6400
|
const canvasRef = React34.useRef(null);
|
|
6258
6401
|
const textRef = React34.useRef(null);
|
|
6259
6402
|
React34.useEffect(() => {
|
|
@@ -6263,6 +6406,7 @@ function PdfPage({ pdfjs, doc, page, scale, textLayer }) {
|
|
|
6263
6406
|
const pg = await doc.getPage(page);
|
|
6264
6407
|
if (cancelled) return;
|
|
6265
6408
|
const viewport = pg.getViewport({ scale });
|
|
6409
|
+
onMeasure?.(page, { width: viewport.width / scale, height: viewport.height / scale });
|
|
6266
6410
|
const canvas = canvasRef.current;
|
|
6267
6411
|
if (!canvas) return;
|
|
6268
6412
|
const ratio = typeof window !== "undefined" ? window.devicePixelRatio || 1 : 1;
|
|
@@ -6279,6 +6423,7 @@ function PdfPage({ pdfjs, doc, page, scale, textLayer }) {
|
|
|
6279
6423
|
}
|
|
6280
6424
|
if (cancelled || !textLayer || !textRef.current || !pdfjs?.TextLayer) return;
|
|
6281
6425
|
try {
|
|
6426
|
+
ensureTextLayerStyles();
|
|
6282
6427
|
textRef.current.innerHTML = "";
|
|
6283
6428
|
textRef.current.style.setProperty("--scale-factor", String(scale));
|
|
6284
6429
|
const tl = new pdfjs.TextLayer({ textContentSource: pg.streamTextContent(), container: textRef.current, viewport });
|
|
@@ -6290,17 +6435,20 @@ function PdfPage({ pdfjs, doc, page, scale, textLayer }) {
|
|
|
6290
6435
|
cancelled = true;
|
|
6291
6436
|
renderTask?.cancel?.();
|
|
6292
6437
|
};
|
|
6293
|
-
}, [pdfjs, doc, page, scale, textLayer]);
|
|
6438
|
+
}, [pdfjs, doc, page, scale, textLayer, onMeasure]);
|
|
6294
6439
|
return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
6295
6440
|
/* @__PURE__ */ jsxRuntime.jsx("canvas", { ref: canvasRef, className: "block bg-white" }),
|
|
6296
|
-
textLayer && /* @__PURE__ */ jsxRuntime.jsx("div", { ref: textRef, className: "textLayer
|
|
6441
|
+
textLayer && /* @__PURE__ */ jsxRuntime.jsx("div", { ref: textRef, className: "oxy-textLayer", "aria-hidden": "true" })
|
|
6297
6442
|
] });
|
|
6298
6443
|
}
|
|
6299
6444
|
var Chevron5 = ({ dir }) => /* @__PURE__ */ jsxRuntime.jsx("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: 2, className: "h-4 w-4", "aria-hidden": "true", children: /* @__PURE__ */ jsxRuntime.jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: dir === "up" ? "m6 15 6-6 6 6" : "m6 9 6 6 6-6" }) });
|
|
6300
|
-
var
|
|
6445
|
+
var MinusIcon = () => /* @__PURE__ */ jsxRuntime.jsx("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: 2, className: "h-4 w-4", "aria-hidden": "true", children: /* @__PURE__ */ jsxRuntime.jsx("path", { strokeLinecap: "round", d: "M5 12h14" }) });
|
|
6446
|
+
var PlusIcon = () => /* @__PURE__ */ jsxRuntime.jsx("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: 2, className: "h-4 w-4", "aria-hidden": "true", children: /* @__PURE__ */ jsxRuntime.jsx("path", { strokeLinecap: "round", d: "M12 5v14M5 12h14" }) });
|
|
6447
|
+
var SearchIcon2 = () => /* @__PURE__ */ jsxRuntime.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: [
|
|
6301
6448
|
/* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "11", cy: "11", r: "7" }),
|
|
6302
6449
|
/* @__PURE__ */ jsxRuntime.jsx("path", { strokeLinecap: "round", d: "m21 21-4.3-4.3" })
|
|
6303
6450
|
] });
|
|
6451
|
+
var CloseIcon = () => /* @__PURE__ */ jsxRuntime.jsx("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: 2, className: "h-4 w-4", "aria-hidden": "true", children: /* @__PURE__ */ jsxRuntime.jsx("path", { strokeLinecap: "round", d: "M6 6l12 12M18 6 6 18" }) });
|
|
6304
6452
|
var DownloadIcon = () => /* @__PURE__ */ jsxRuntime.jsx("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: 2, className: "h-4 w-4", "aria-hidden": "true", children: /* @__PURE__ */ jsxRuntime.jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M12 3v12m0 0 4-4m-4 4-4-4M4 21h16" }) });
|
|
6305
6453
|
var PrintIcon = () => /* @__PURE__ */ jsxRuntime.jsx("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: 2, className: "h-4 w-4", "aria-hidden": "true", children: /* @__PURE__ */ jsxRuntime.jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M6 9V3h12v6M6 18H4v-7h16v7h-2M8 14h8v7H8z" }) });
|
|
6306
6454
|
var FitWidthIcon = () => /* @__PURE__ */ jsxRuntime.jsx("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: 2, className: "h-4 w-4", "aria-hidden": "true", children: /* @__PURE__ */ jsxRuntime.jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M3 12h18m0 0-4-4m4 4-4 4M3 12l4-4m-4 4 4 4" }) });
|
|
@@ -6311,10 +6459,10 @@ var FitPageIcon = () => /* @__PURE__ */ jsxRuntime.jsxs("svg", { viewBox: "0 0 2
|
|
|
6311
6459
|
var DEFAULT_COL_WIDTH = 140;
|
|
6312
6460
|
var GUTTER = 52;
|
|
6313
6461
|
function resolveWidth(w) {
|
|
6314
|
-
if (typeof w === "number") return w;
|
|
6462
|
+
if (typeof w === "number" && Number.isFinite(w)) return w;
|
|
6315
6463
|
if (typeof w === "string") {
|
|
6316
|
-
const
|
|
6317
|
-
if (
|
|
6464
|
+
const m = /^\s*(\d+(?:\.\d+)?)(px)?\s*$/.exec(w);
|
|
6465
|
+
if (m) return parseFloat(m[1]);
|
|
6318
6466
|
}
|
|
6319
6467
|
return DEFAULT_COL_WIDTH;
|
|
6320
6468
|
}
|
|
@@ -6507,13 +6655,16 @@ var xlsxPromise = null;
|
|
|
6507
6655
|
var loadXlsx = () => xlsxPromise ??= import('xlsx');
|
|
6508
6656
|
var jspdfPromise = null;
|
|
6509
6657
|
var loadJspdf = () => jspdfPromise ??= import('jspdf');
|
|
6510
|
-
function
|
|
6511
|
-
|
|
6512
|
-
|
|
6513
|
-
|
|
6514
|
-
|
|
6515
|
-
|
|
6516
|
-
|
|
6658
|
+
function coerceToCellType(prev, next) {
|
|
6659
|
+
if (typeof prev === "number") {
|
|
6660
|
+
const n = Number(next);
|
|
6661
|
+
return next.trim() !== "" && Number.isFinite(n) ? n : next;
|
|
6662
|
+
}
|
|
6663
|
+
if (typeof prev === "boolean") {
|
|
6664
|
+
if (/^(true|false)$/i.test(next.trim())) return next.trim().toLowerCase() === "true";
|
|
6665
|
+
return next;
|
|
6666
|
+
}
|
|
6667
|
+
return next;
|
|
6517
6668
|
}
|
|
6518
6669
|
function Spreadsheet({
|
|
6519
6670
|
source,
|
|
@@ -6581,20 +6732,23 @@ function Spreadsheet({
|
|
|
6581
6732
|
const columns = React34.useMemo(() => sheet ? toColumns(sheet.columns) : [], [sheet]);
|
|
6582
6733
|
const plainRows = React34.useMemo(() => sheet ? toPlainRows(sheet, columns) : [], [sheet, columns]);
|
|
6583
6734
|
const handleCellEdit = React34.useCallback(({ row, column, value }) => {
|
|
6735
|
+
let coerced = value;
|
|
6584
6736
|
setSheets((prev) => {
|
|
6585
6737
|
if (!prev) return prev;
|
|
6586
6738
|
const next = prev.map((s, i) => i === active ? { ...s, rows: s.rows.map((r) => ({ ...r })) } : s);
|
|
6587
6739
|
const target = next[active];
|
|
6588
6740
|
const existing = target.rows[row]?.[column];
|
|
6741
|
+
const prevValue = cellValue(existing);
|
|
6742
|
+
coerced = coerceToCellType(prevValue, value);
|
|
6589
6743
|
if (existing != null && typeof existing === "object" && "value" in existing) {
|
|
6590
|
-
target.rows[row][column] = { ...existing, value };
|
|
6744
|
+
target.rows[row][column] = { ...existing, value: coerced };
|
|
6591
6745
|
} else {
|
|
6592
|
-
target.rows[row][column] =
|
|
6746
|
+
target.rows[row][column] = coerced;
|
|
6593
6747
|
}
|
|
6594
6748
|
onChange?.(next);
|
|
6595
6749
|
return next;
|
|
6596
6750
|
});
|
|
6597
|
-
onCellEdit?.({ sheet: sheets?.[active]?.name ?? "", row, column, value });
|
|
6751
|
+
onCellEdit?.({ sheet: sheets?.[active]?.name ?? "", row, column, value: coerced });
|
|
6598
6752
|
}, [active, onCellEdit, onChange, sheets]);
|
|
6599
6753
|
const baseName = React34.useMemo(
|
|
6600
6754
|
() => (fileName || (typeof source === "object" && "name" in source ? sourceName(source) : null) || "spreadsheet").replace(/\.[^.]+$/, ""),
|
|
@@ -6615,7 +6769,7 @@ function Spreadsheet({
|
|
|
6615
6769
|
XLSX.utils.book_append_sheet(wb, ws, (s.name || "Sheet").slice(0, 31));
|
|
6616
6770
|
});
|
|
6617
6771
|
const out = XLSX.write(wb, { bookType: "xlsx", type: "array" });
|
|
6618
|
-
|
|
6772
|
+
downloadBlob(new Blob([out], { type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" }), `${baseName}.xlsx`);
|
|
6619
6773
|
}, [sheets, sheetAoa, baseName]);
|
|
6620
6774
|
const exportCsv = React34.useCallback(() => {
|
|
6621
6775
|
if (!sheet) return;
|
|
@@ -6625,7 +6779,7 @@ function Spreadsheet({
|
|
|
6625
6779
|
return /[",\n]/.test(s) ? `"${s.replace(/"/g, '""')}"` : s;
|
|
6626
6780
|
};
|
|
6627
6781
|
const csv = aoa.map((row) => row.map(esc).join(",")).join("\r\n");
|
|
6628
|
-
|
|
6782
|
+
downloadBlob(new Blob([`\uFEFF${csv}`], { type: "text/csv;charset=utf-8" }), `${baseName}.csv`);
|
|
6629
6783
|
}, [sheet, sheetAoa, baseName]);
|
|
6630
6784
|
const exportPdf = React34.useCallback(async () => {
|
|
6631
6785
|
if (!sheet) return;
|
|
@@ -6681,9 +6835,14 @@ function Spreadsheet({
|
|
|
6681
6835
|
] });
|
|
6682
6836
|
}
|
|
6683
6837
|
const formats = exportFormats || [];
|
|
6684
|
-
|
|
6685
|
-
|
|
6686
|
-
|
|
6838
|
+
const exportLabels = {
|
|
6839
|
+
xlsx: "Excel workbook (.xlsx)",
|
|
6840
|
+
csv: "CSV \u2014 this sheet (.csv)",
|
|
6841
|
+
pdf: "PDF table (.pdf)"
|
|
6842
|
+
};
|
|
6843
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: cx("flex flex-col overflow-hidden rounded-lg border border-border bg-surface-raised", className), style, children: [
|
|
6844
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-shrink-0 items-center gap-2 border-b border-border bg-surface px-2 py-1.5", children: [
|
|
6845
|
+
sheets.length > 1 ? /* @__PURE__ */ jsxRuntime.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__ */ jsxRuntime.jsx(
|
|
6687
6846
|
"button",
|
|
6688
6847
|
{
|
|
6689
6848
|
role: "tab",
|
|
@@ -6691,14 +6850,40 @@ function Spreadsheet({
|
|
|
6691
6850
|
"aria-selected": i === active,
|
|
6692
6851
|
onClick: () => setActive(i),
|
|
6693
6852
|
className: cx(
|
|
6694
|
-
"rounded-md px-3 py-1 text-sm transition-colors",
|
|
6695
|
-
|
|
6853
|
+
"flex-shrink-0 rounded-md px-3 py-1 text-sm font-medium transition-colors",
|
|
6854
|
+
"focus:outline-none focus-visible:ring-2 focus-visible:ring-accent",
|
|
6855
|
+
i === active ? "bg-accent text-accent-fg shadow-sm" : "text-foreground-secondary hover:bg-surface-raised hover:text-foreground"
|
|
6696
6856
|
),
|
|
6697
6857
|
children: s.name || `Sheet ${i + 1}`
|
|
6698
6858
|
},
|
|
6699
6859
|
`${s.name}-${i}`
|
|
6700
|
-
)) }),
|
|
6701
|
-
|
|
6860
|
+
)) }) : /* @__PURE__ */ jsxRuntime.jsx("span", { className: "flex-1 truncate px-1 text-sm font-medium text-foreground", children: sheet?.name || "Sheet 1" }),
|
|
6861
|
+
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: "hidden flex-shrink-0 px-1 text-xs tabular-nums text-foreground-muted sm:inline", children: [
|
|
6862
|
+
plainRows.length.toLocaleString(),
|
|
6863
|
+
" ",
|
|
6864
|
+
plainRows.length === 1 ? "row" : "rows",
|
|
6865
|
+
" \xB7 ",
|
|
6866
|
+
columns.length,
|
|
6867
|
+
" cols"
|
|
6868
|
+
] }),
|
|
6869
|
+
formats.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
6870
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "h-5 w-px flex-shrink-0 bg-border", "aria-hidden": "true" }),
|
|
6871
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
6872
|
+
MenuButton,
|
|
6873
|
+
{
|
|
6874
|
+
label: "Export",
|
|
6875
|
+
size: "sm",
|
|
6876
|
+
variant: "outline",
|
|
6877
|
+
align: "end",
|
|
6878
|
+
icon: /* @__PURE__ */ jsxRuntime.jsx(DownloadIcon2, {}),
|
|
6879
|
+
items: formats.map((fmt) => ({
|
|
6880
|
+
key: fmt,
|
|
6881
|
+
label: exportLabels[fmt],
|
|
6882
|
+
onSelect: () => runExport(fmt)
|
|
6883
|
+
}))
|
|
6884
|
+
}
|
|
6885
|
+
)
|
|
6886
|
+
] })
|
|
6702
6887
|
] }),
|
|
6703
6888
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
6704
6889
|
DataGrid,
|
|
@@ -6707,11 +6892,13 @@ function Spreadsheet({
|
|
|
6707
6892
|
rows: plainRows,
|
|
6708
6893
|
editable,
|
|
6709
6894
|
virtualize,
|
|
6710
|
-
onCellEdit: handleCellEdit
|
|
6895
|
+
onCellEdit: handleCellEdit,
|
|
6896
|
+
className: "!rounded-none !border-0"
|
|
6711
6897
|
}
|
|
6712
6898
|
)
|
|
6713
6899
|
] });
|
|
6714
6900
|
}
|
|
6901
|
+
var DownloadIcon2 = () => /* @__PURE__ */ jsxRuntime.jsx("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: 2, className: "h-4 w-4", "aria-hidden": "true", children: /* @__PURE__ */ jsxRuntime.jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M12 3v12m0 0 4-4m-4 4-4-4M4 21h16" }) });
|
|
6715
6902
|
function ThemeSwitch({ checked, onChange, label = "Toggle dark mode", className = "" }) {
|
|
6716
6903
|
const id = React34.useId();
|
|
6717
6904
|
return /* @__PURE__ */ jsxRuntime.jsx("label", { htmlFor: id, className: `flex items-center gap-2 cursor-pointer select-none ${className}`.trim(), children: /* @__PURE__ */ jsxRuntime.jsx(
|