@cherrystudio/file-preview 0.0.0-stage → 0.1.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/LICENSE +661 -0
- package/README.md +247 -2
- package/dist/Combination-BLg27FvF.js +1044 -0
- package/dist/EchartsChartRenderer-e2b5JysX.js +39859 -0
- package/dist/FilePreviewTooLarge-DkHa6toZ.js +27 -0
- package/dist/ImageFilePreview-BO8rLRZv.js +2171 -0
- package/dist/PdfFilePreview-D3Ie0_mm.js +29747 -0
- package/dist/PowerPointFilePreview-BLfF0zcm.js +58032 -0
- package/dist/SpreadsheetFilePreview-DJdoyeis.js +3217 -0
- package/dist/WordFilePreview-DSqt7Uq2.js +4137 -0
- package/dist/assets/cmaps/78-EUC-H.bcmap +0 -0
- package/dist/assets/cmaps/78-EUC-V.bcmap +0 -0
- package/dist/assets/cmaps/78-H.bcmap +0 -0
- package/dist/assets/cmaps/78-RKSJ-H.bcmap +0 -0
- package/dist/assets/cmaps/78-RKSJ-V.bcmap +0 -0
- package/dist/assets/cmaps/78-V.bcmap +0 -0
- package/dist/assets/cmaps/78ms-RKSJ-H.bcmap +0 -0
- package/dist/assets/cmaps/78ms-RKSJ-V.bcmap +0 -0
- package/dist/assets/cmaps/83pv-RKSJ-H.bcmap +0 -0
- package/dist/assets/cmaps/90ms-RKSJ-H.bcmap +0 -0
- package/dist/assets/cmaps/90ms-RKSJ-V.bcmap +0 -0
- package/dist/assets/cmaps/90msp-RKSJ-H.bcmap +0 -0
- package/dist/assets/cmaps/90msp-RKSJ-V.bcmap +0 -0
- package/dist/assets/cmaps/90pv-RKSJ-H.bcmap +0 -0
- package/dist/assets/cmaps/90pv-RKSJ-V.bcmap +0 -0
- package/dist/assets/cmaps/Add-H.bcmap +0 -0
- package/dist/assets/cmaps/Add-RKSJ-H.bcmap +0 -0
- package/dist/assets/cmaps/Add-RKSJ-V.bcmap +0 -0
- package/dist/assets/cmaps/Add-V.bcmap +0 -0
- package/dist/assets/cmaps/Adobe-CNS1-0.bcmap +0 -0
- package/dist/assets/cmaps/Adobe-CNS1-1.bcmap +0 -0
- package/dist/assets/cmaps/Adobe-CNS1-2.bcmap +0 -0
- package/dist/assets/cmaps/Adobe-CNS1-3.bcmap +0 -0
- package/dist/assets/cmaps/Adobe-CNS1-4.bcmap +0 -0
- package/dist/assets/cmaps/Adobe-CNS1-5.bcmap +0 -0
- package/dist/assets/cmaps/Adobe-CNS1-6.bcmap +0 -0
- package/dist/assets/cmaps/Adobe-CNS1-UCS2.bcmap +0 -0
- package/dist/assets/cmaps/Adobe-GB1-0.bcmap +0 -0
- package/dist/assets/cmaps/Adobe-GB1-1.bcmap +0 -0
- package/dist/assets/cmaps/Adobe-GB1-2.bcmap +0 -0
- package/dist/assets/cmaps/Adobe-GB1-3.bcmap +0 -0
- package/dist/assets/cmaps/Adobe-GB1-4.bcmap +0 -0
- package/dist/assets/cmaps/Adobe-GB1-5.bcmap +0 -0
- package/dist/assets/cmaps/Adobe-GB1-UCS2.bcmap +0 -0
- package/dist/assets/cmaps/Adobe-Japan1-0.bcmap +0 -0
- package/dist/assets/cmaps/Adobe-Japan1-1.bcmap +0 -0
- package/dist/assets/cmaps/Adobe-Japan1-2.bcmap +0 -0
- package/dist/assets/cmaps/Adobe-Japan1-3.bcmap +0 -0
- package/dist/assets/cmaps/Adobe-Japan1-4.bcmap +0 -0
- package/dist/assets/cmaps/Adobe-Japan1-5.bcmap +0 -0
- package/dist/assets/cmaps/Adobe-Japan1-6.bcmap +0 -0
- package/dist/assets/cmaps/Adobe-Japan1-UCS2.bcmap +0 -0
- package/dist/assets/cmaps/Adobe-Korea1-0.bcmap +0 -0
- package/dist/assets/cmaps/Adobe-Korea1-1.bcmap +0 -0
- package/dist/assets/cmaps/Adobe-Korea1-2.bcmap +0 -0
- package/dist/assets/cmaps/Adobe-Korea1-UCS2.bcmap +0 -0
- package/dist/assets/cmaps/B5-H.bcmap +0 -0
- package/dist/assets/cmaps/B5-V.bcmap +0 -0
- package/dist/assets/cmaps/B5pc-H.bcmap +0 -0
- package/dist/assets/cmaps/B5pc-V.bcmap +0 -0
- package/dist/assets/cmaps/CNS-EUC-H.bcmap +0 -0
- package/dist/assets/cmaps/CNS-EUC-V.bcmap +0 -0
- package/dist/assets/cmaps/CNS1-H.bcmap +0 -0
- package/dist/assets/cmaps/CNS1-V.bcmap +0 -0
- package/dist/assets/cmaps/CNS2-H.bcmap +0 -0
- package/dist/assets/cmaps/CNS2-V.bcmap +3 -0
- package/dist/assets/cmaps/ETHK-B5-H.bcmap +0 -0
- package/dist/assets/cmaps/ETHK-B5-V.bcmap +0 -0
- package/dist/assets/cmaps/ETen-B5-H.bcmap +0 -0
- package/dist/assets/cmaps/ETen-B5-V.bcmap +0 -0
- package/dist/assets/cmaps/ETenms-B5-H.bcmap +3 -0
- package/dist/assets/cmaps/ETenms-B5-V.bcmap +0 -0
- package/dist/assets/cmaps/EUC-H.bcmap +0 -0
- package/dist/assets/cmaps/EUC-V.bcmap +0 -0
- package/dist/assets/cmaps/Ext-H.bcmap +0 -0
- package/dist/assets/cmaps/Ext-RKSJ-H.bcmap +0 -0
- package/dist/assets/cmaps/Ext-RKSJ-V.bcmap +0 -0
- package/dist/assets/cmaps/Ext-V.bcmap +0 -0
- package/dist/assets/cmaps/GB-EUC-H.bcmap +0 -0
- package/dist/assets/cmaps/GB-EUC-V.bcmap +0 -0
- package/dist/assets/cmaps/GB-H.bcmap +4 -0
- package/dist/assets/cmaps/GB-V.bcmap +0 -0
- package/dist/assets/cmaps/GBK-EUC-H.bcmap +0 -0
- package/dist/assets/cmaps/GBK-EUC-V.bcmap +0 -0
- package/dist/assets/cmaps/GBK2K-H.bcmap +0 -0
- package/dist/assets/cmaps/GBK2K-V.bcmap +0 -0
- package/dist/assets/cmaps/GBKp-EUC-H.bcmap +0 -0
- package/dist/assets/cmaps/GBKp-EUC-V.bcmap +0 -0
- package/dist/assets/cmaps/GBT-EUC-H.bcmap +0 -0
- package/dist/assets/cmaps/GBT-EUC-V.bcmap +0 -0
- package/dist/assets/cmaps/GBT-H.bcmap +0 -0
- package/dist/assets/cmaps/GBT-V.bcmap +0 -0
- package/dist/assets/cmaps/GBTpc-EUC-H.bcmap +0 -0
- package/dist/assets/cmaps/GBTpc-EUC-V.bcmap +0 -0
- package/dist/assets/cmaps/GBpc-EUC-H.bcmap +0 -0
- package/dist/assets/cmaps/GBpc-EUC-V.bcmap +0 -0
- package/dist/assets/cmaps/H.bcmap +0 -0
- package/dist/assets/cmaps/HKdla-B5-H.bcmap +0 -0
- package/dist/assets/cmaps/HKdla-B5-V.bcmap +0 -0
- package/dist/assets/cmaps/HKdlb-B5-H.bcmap +0 -0
- package/dist/assets/cmaps/HKdlb-B5-V.bcmap +0 -0
- package/dist/assets/cmaps/HKgccs-B5-H.bcmap +0 -0
- package/dist/assets/cmaps/HKgccs-B5-V.bcmap +0 -0
- package/dist/assets/cmaps/HKm314-B5-H.bcmap +0 -0
- package/dist/assets/cmaps/HKm314-B5-V.bcmap +0 -0
- package/dist/assets/cmaps/HKm471-B5-H.bcmap +0 -0
- package/dist/assets/cmaps/HKm471-B5-V.bcmap +0 -0
- package/dist/assets/cmaps/HKscs-B5-H.bcmap +0 -0
- package/dist/assets/cmaps/HKscs-B5-V.bcmap +0 -0
- package/dist/assets/cmaps/Hankaku.bcmap +0 -0
- package/dist/assets/cmaps/Hiragana.bcmap +0 -0
- package/dist/assets/cmaps/KSC-EUC-H.bcmap +0 -0
- package/dist/assets/cmaps/KSC-EUC-V.bcmap +0 -0
- package/dist/assets/cmaps/KSC-H.bcmap +0 -0
- package/dist/assets/cmaps/KSC-Johab-H.bcmap +0 -0
- package/dist/assets/cmaps/KSC-Johab-V.bcmap +0 -0
- package/dist/assets/cmaps/KSC-V.bcmap +0 -0
- package/dist/assets/cmaps/KSCms-UHC-H.bcmap +0 -0
- package/dist/assets/cmaps/KSCms-UHC-HW-H.bcmap +0 -0
- package/dist/assets/cmaps/KSCms-UHC-HW-V.bcmap +0 -0
- package/dist/assets/cmaps/KSCms-UHC-V.bcmap +0 -0
- package/dist/assets/cmaps/KSCpc-EUC-H.bcmap +0 -0
- package/dist/assets/cmaps/KSCpc-EUC-V.bcmap +0 -0
- package/dist/assets/cmaps/Katakana.bcmap +0 -0
- package/dist/assets/cmaps/LICENSE +36 -0
- package/dist/assets/cmaps/NWP-H.bcmap +0 -0
- package/dist/assets/cmaps/NWP-V.bcmap +0 -0
- package/dist/assets/cmaps/RKSJ-H.bcmap +0 -0
- package/dist/assets/cmaps/RKSJ-V.bcmap +0 -0
- package/dist/assets/cmaps/Roman.bcmap +0 -0
- package/dist/assets/cmaps/UniCNS-UCS2-H.bcmap +0 -0
- package/dist/assets/cmaps/UniCNS-UCS2-V.bcmap +0 -0
- package/dist/assets/cmaps/UniCNS-UTF16-H.bcmap +0 -0
- package/dist/assets/cmaps/UniCNS-UTF16-V.bcmap +0 -0
- package/dist/assets/cmaps/UniCNS-UTF32-H.bcmap +0 -0
- package/dist/assets/cmaps/UniCNS-UTF32-V.bcmap +0 -0
- package/dist/assets/cmaps/UniCNS-UTF8-H.bcmap +0 -0
- package/dist/assets/cmaps/UniCNS-UTF8-V.bcmap +0 -0
- package/dist/assets/cmaps/UniGB-UCS2-H.bcmap +0 -0
- package/dist/assets/cmaps/UniGB-UCS2-V.bcmap +0 -0
- package/dist/assets/cmaps/UniGB-UTF16-H.bcmap +0 -0
- package/dist/assets/cmaps/UniGB-UTF16-V.bcmap +0 -0
- package/dist/assets/cmaps/UniGB-UTF32-H.bcmap +0 -0
- package/dist/assets/cmaps/UniGB-UTF32-V.bcmap +0 -0
- package/dist/assets/cmaps/UniGB-UTF8-H.bcmap +0 -0
- package/dist/assets/cmaps/UniGB-UTF8-V.bcmap +0 -0
- package/dist/assets/cmaps/UniJIS-UCS2-H.bcmap +0 -0
- package/dist/assets/cmaps/UniJIS-UCS2-HW-H.bcmap +0 -0
- package/dist/assets/cmaps/UniJIS-UCS2-HW-V.bcmap +0 -0
- package/dist/assets/cmaps/UniJIS-UCS2-V.bcmap +0 -0
- package/dist/assets/cmaps/UniJIS-UTF16-H.bcmap +0 -0
- package/dist/assets/cmaps/UniJIS-UTF16-V.bcmap +0 -0
- package/dist/assets/cmaps/UniJIS-UTF32-H.bcmap +0 -0
- package/dist/assets/cmaps/UniJIS-UTF32-V.bcmap +0 -0
- package/dist/assets/cmaps/UniJIS-UTF8-H.bcmap +0 -0
- package/dist/assets/cmaps/UniJIS-UTF8-V.bcmap +0 -0
- package/dist/assets/cmaps/UniJIS2004-UTF16-H.bcmap +0 -0
- package/dist/assets/cmaps/UniJIS2004-UTF16-V.bcmap +0 -0
- package/dist/assets/cmaps/UniJIS2004-UTF32-H.bcmap +0 -0
- package/dist/assets/cmaps/UniJIS2004-UTF32-V.bcmap +0 -0
- package/dist/assets/cmaps/UniJIS2004-UTF8-H.bcmap +0 -0
- package/dist/assets/cmaps/UniJIS2004-UTF8-V.bcmap +0 -0
- package/dist/assets/cmaps/UniJISPro-UCS2-HW-V.bcmap +0 -0
- package/dist/assets/cmaps/UniJISPro-UCS2-V.bcmap +0 -0
- package/dist/assets/cmaps/UniJISPro-UTF8-V.bcmap +0 -0
- package/dist/assets/cmaps/UniJISX0213-UTF32-H.bcmap +0 -0
- package/dist/assets/cmaps/UniJISX0213-UTF32-V.bcmap +0 -0
- package/dist/assets/cmaps/UniJISX02132004-UTF32-H.bcmap +0 -0
- package/dist/assets/cmaps/UniJISX02132004-UTF32-V.bcmap +0 -0
- package/dist/assets/cmaps/UniKS-UCS2-H.bcmap +0 -0
- package/dist/assets/cmaps/UniKS-UCS2-V.bcmap +0 -0
- package/dist/assets/cmaps/UniKS-UTF16-H.bcmap +0 -0
- package/dist/assets/cmaps/UniKS-UTF16-V.bcmap +0 -0
- package/dist/assets/cmaps/UniKS-UTF32-H.bcmap +0 -0
- package/dist/assets/cmaps/UniKS-UTF32-V.bcmap +0 -0
- package/dist/assets/cmaps/UniKS-UTF8-H.bcmap +0 -0
- package/dist/assets/cmaps/UniKS-UTF8-V.bcmap +0 -0
- package/dist/assets/cmaps/V.bcmap +0 -0
- package/dist/assets/cmaps/WP-Symbol.bcmap +0 -0
- package/dist/assets/pdf.worker.js +77187 -0
- package/dist/assets/standard_fonts/FoxitDingbats.pfb +0 -0
- package/dist/assets/standard_fonts/FoxitFixed.pfb +0 -0
- package/dist/assets/standard_fonts/FoxitFixedBold.pfb +0 -0
- package/dist/assets/standard_fonts/FoxitFixedBoldItalic.pfb +0 -0
- package/dist/assets/standard_fonts/FoxitFixedItalic.pfb +0 -0
- package/dist/assets/standard_fonts/FoxitSerif.pfb +0 -0
- package/dist/assets/standard_fonts/FoxitSerifBold.pfb +0 -0
- package/dist/assets/standard_fonts/FoxitSerifBoldItalic.pfb +0 -0
- package/dist/assets/standard_fonts/FoxitSerifItalic.pfb +0 -0
- package/dist/assets/standard_fonts/FoxitSymbol.pfb +0 -0
- package/dist/assets/standard_fonts/LICENSE_FOXIT +27 -0
- package/dist/assets/standard_fonts/LICENSE_LIBERATION +102 -0
- package/dist/assets/standard_fonts/LiberationSans-Bold.ttf +0 -0
- package/dist/assets/standard_fonts/LiberationSans-BoldItalic.ttf +0 -0
- package/dist/assets/standard_fonts/LiberationSans-Italic.ttf +0 -0
- package/dist/assets/standard_fonts/LiberationSans-Regular.ttf +0 -0
- package/dist/assets/xlsx.worker.js +61444 -0
- package/dist/core.d.ts +2 -0
- package/dist/core.js +3 -0
- package/dist/documentAnchor-J7GGLU15.js +69 -0
- package/dist/imageFilePreviewPlugin-CxIUlEmg.js +29 -0
- package/dist/jszip.min-uwqlc4KL.js +3108 -0
- package/dist/react.d.ts +105 -0
- package/dist/react.js +3114 -0
- package/dist/rolldown-runtime-B-lAHAz2.js +37 -0
- package/dist/rotate-ccw-YWx7RzY9.js +36 -0
- package/dist/selection-CQPD5a6C.js +47 -0
- package/dist/source-DjjzeN4t.d.ts +58 -0
- package/dist/source-Rx3etU3y.js +28 -0
- package/dist/styles.css +8778 -0
- package/dist/styles.js +0 -0
- package/dist/touchPinch-C4fl-9fg.js +34 -0
- package/dist/useTranslation-DMhY277o.js +7056 -0
- package/dist/zoom-out-DLOQGt1e.js +66 -0
- package/package.json +69 -3
|
@@ -0,0 +1,3217 @@
|
|
|
1
|
+
import "./documentAnchor-J7GGLU15.js";
|
|
2
|
+
import { r as readPreviewDocument, t as PreviewError } from "./source-Rx3etU3y.js";
|
|
3
|
+
import { F as cn, I as LoaderCircle, N as EmptyState, i as FilePreviewLayout, o as usePreviewHost, r as FilePreviewToolbarButton, s as usePreviewLogger, t as useTranslation, z as cva } from "./useTranslation-DMhY277o.js";
|
|
4
|
+
import { n as normalizeSelectionText, r as CircleAlert, t as createPreviewSelection } from "./selection-CQPD5a6C.js";
|
|
5
|
+
import { n as ZoomIn, t as ZoomOut } from "./zoom-out-DLOQGt1e.js";
|
|
6
|
+
import { t as FilePreviewTooLarge } from "./FilePreviewTooLarge-DkHa6toZ.js";
|
|
7
|
+
import * as React$1 from "react";
|
|
8
|
+
import React, { memo, useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|
9
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
10
|
+
import { flushSync } from "react-dom";
|
|
11
|
+
typeof window !== "undefined" && window.document && window.document.createElement;
|
|
12
|
+
function composeEventHandlers(originalEventHandler, ourEventHandler, { checkForDefaultPrevented = true } = {}) {
|
|
13
|
+
return function handleEvent(event) {
|
|
14
|
+
originalEventHandler?.(event);
|
|
15
|
+
if (checkForDefaultPrevented === false || !event.defaultPrevented) return ourEventHandler?.(event);
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
//#endregion
|
|
19
|
+
//#region ../../node_modules/.pnpm/@radix-ui+react-context@1.1.2_@types+react@19.2.7_react@19.2.3/node_modules/@radix-ui/react-context/dist/index.mjs
|
|
20
|
+
function createContextScope(scopeName, createContextScopeDeps = []) {
|
|
21
|
+
let defaultContexts = [];
|
|
22
|
+
function createContext3(rootComponentName, defaultContext) {
|
|
23
|
+
const BaseContext = React$1.createContext(defaultContext);
|
|
24
|
+
const index = defaultContexts.length;
|
|
25
|
+
defaultContexts = [...defaultContexts, defaultContext];
|
|
26
|
+
const Provider = (props) => {
|
|
27
|
+
const { scope, children, ...context } = props;
|
|
28
|
+
const Context = scope?.[scopeName]?.[index] || BaseContext;
|
|
29
|
+
const value = React$1.useMemo(() => context, Object.values(context));
|
|
30
|
+
return /* @__PURE__ */ jsx(Context.Provider, {
|
|
31
|
+
value,
|
|
32
|
+
children
|
|
33
|
+
});
|
|
34
|
+
};
|
|
35
|
+
Provider.displayName = rootComponentName + "Provider";
|
|
36
|
+
function useContext2(consumerName, scope) {
|
|
37
|
+
const Context = scope?.[scopeName]?.[index] || BaseContext;
|
|
38
|
+
const context = React$1.useContext(Context);
|
|
39
|
+
if (context) return context;
|
|
40
|
+
if (defaultContext !== void 0) return defaultContext;
|
|
41
|
+
throw new Error(`\`${consumerName}\` must be used within \`${rootComponentName}\``);
|
|
42
|
+
}
|
|
43
|
+
return [Provider, useContext2];
|
|
44
|
+
}
|
|
45
|
+
const createScope = () => {
|
|
46
|
+
const scopeContexts = defaultContexts.map((defaultContext) => {
|
|
47
|
+
return React$1.createContext(defaultContext);
|
|
48
|
+
});
|
|
49
|
+
return function useScope(scope) {
|
|
50
|
+
const contexts = scope?.[scopeName] || scopeContexts;
|
|
51
|
+
return React$1.useMemo(() => ({ [`__scope${scopeName}`]: {
|
|
52
|
+
...scope,
|
|
53
|
+
[scopeName]: contexts
|
|
54
|
+
} }), [scope, contexts]);
|
|
55
|
+
};
|
|
56
|
+
};
|
|
57
|
+
createScope.scopeName = scopeName;
|
|
58
|
+
return [createContext3, composeContextScopes(createScope, ...createContextScopeDeps)];
|
|
59
|
+
}
|
|
60
|
+
function composeContextScopes(...scopes) {
|
|
61
|
+
const baseScope = scopes[0];
|
|
62
|
+
if (scopes.length === 1) return baseScope;
|
|
63
|
+
const createScope = () => {
|
|
64
|
+
const scopeHooks = scopes.map((createScope2) => ({
|
|
65
|
+
useScope: createScope2(),
|
|
66
|
+
scopeName: createScope2.scopeName
|
|
67
|
+
}));
|
|
68
|
+
return function useComposedScopes(overrideScopes) {
|
|
69
|
+
const nextScopes = scopeHooks.reduce((nextScopes2, { useScope, scopeName }) => {
|
|
70
|
+
const currentScope = useScope(overrideScopes)[`__scope${scopeName}`];
|
|
71
|
+
return {
|
|
72
|
+
...nextScopes2,
|
|
73
|
+
...currentScope
|
|
74
|
+
};
|
|
75
|
+
}, {});
|
|
76
|
+
return React$1.useMemo(() => ({ [`__scope${baseScope.scopeName}`]: nextScopes }), [nextScopes]);
|
|
77
|
+
};
|
|
78
|
+
};
|
|
79
|
+
createScope.scopeName = baseScope.scopeName;
|
|
80
|
+
return createScope;
|
|
81
|
+
}
|
|
82
|
+
//#endregion
|
|
83
|
+
//#region ../../node_modules/.pnpm/@radix-ui+react-compose-refs@1.1.2_@types+react@19.2.7_react@19.2.3/node_modules/@radix-ui/react-compose-refs/dist/index.mjs
|
|
84
|
+
function setRef(ref, value) {
|
|
85
|
+
if (typeof ref === "function") return ref(value);
|
|
86
|
+
else if (ref !== null && ref !== void 0) ref.current = value;
|
|
87
|
+
}
|
|
88
|
+
function composeRefs(...refs) {
|
|
89
|
+
return (node) => {
|
|
90
|
+
let hasCleanup = false;
|
|
91
|
+
const cleanups = refs.map((ref) => {
|
|
92
|
+
const cleanup = setRef(ref, node);
|
|
93
|
+
if (!hasCleanup && typeof cleanup == "function") hasCleanup = true;
|
|
94
|
+
return cleanup;
|
|
95
|
+
});
|
|
96
|
+
if (hasCleanup) return () => {
|
|
97
|
+
for (let i = 0; i < cleanups.length; i++) {
|
|
98
|
+
const cleanup = cleanups[i];
|
|
99
|
+
if (typeof cleanup == "function") cleanup();
|
|
100
|
+
else setRef(refs[i], null);
|
|
101
|
+
}
|
|
102
|
+
};
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
function useComposedRefs(...refs) {
|
|
106
|
+
return React$1.useCallback(composeRefs(...refs), refs);
|
|
107
|
+
}
|
|
108
|
+
//#endregion
|
|
109
|
+
//#region ../../node_modules/.pnpm/@radix-ui+react-slot@1.2.3_@types+react@19.2.7_react@19.2.3/node_modules/@radix-ui/react-slot/dist/index.mjs
|
|
110
|
+
// @__NO_SIDE_EFFECTS__
|
|
111
|
+
function createSlot(ownerName) {
|
|
112
|
+
const SlotClone = /* @__PURE__ */ createSlotClone(ownerName);
|
|
113
|
+
const Slot2 = React$1.forwardRef((props, forwardedRef) => {
|
|
114
|
+
const { children, ...slotProps } = props;
|
|
115
|
+
const childrenArray = React$1.Children.toArray(children);
|
|
116
|
+
const slottable = childrenArray.find(isSlottable);
|
|
117
|
+
if (slottable) {
|
|
118
|
+
const newElement = slottable.props.children;
|
|
119
|
+
const newChildren = childrenArray.map((child) => {
|
|
120
|
+
if (child === slottable) {
|
|
121
|
+
if (React$1.Children.count(newElement) > 1) return React$1.Children.only(null);
|
|
122
|
+
return React$1.isValidElement(newElement) ? newElement.props.children : null;
|
|
123
|
+
} else return child;
|
|
124
|
+
});
|
|
125
|
+
return /* @__PURE__ */ jsx(SlotClone, {
|
|
126
|
+
...slotProps,
|
|
127
|
+
ref: forwardedRef,
|
|
128
|
+
children: React$1.isValidElement(newElement) ? React$1.cloneElement(newElement, void 0, newChildren) : null
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
return /* @__PURE__ */ jsx(SlotClone, {
|
|
132
|
+
...slotProps,
|
|
133
|
+
ref: forwardedRef,
|
|
134
|
+
children
|
|
135
|
+
});
|
|
136
|
+
});
|
|
137
|
+
Slot2.displayName = `${ownerName}.Slot`;
|
|
138
|
+
return Slot2;
|
|
139
|
+
}
|
|
140
|
+
// @__NO_SIDE_EFFECTS__
|
|
141
|
+
function createSlotClone(ownerName) {
|
|
142
|
+
const SlotClone = React$1.forwardRef((props, forwardedRef) => {
|
|
143
|
+
const { children, ...slotProps } = props;
|
|
144
|
+
if (React$1.isValidElement(children)) {
|
|
145
|
+
const childrenRef = getElementRef$1(children);
|
|
146
|
+
const props2 = mergeProps(slotProps, children.props);
|
|
147
|
+
if (children.type !== React$1.Fragment) props2.ref = forwardedRef ? composeRefs(forwardedRef, childrenRef) : childrenRef;
|
|
148
|
+
return React$1.cloneElement(children, props2);
|
|
149
|
+
}
|
|
150
|
+
return React$1.Children.count(children) > 1 ? React$1.Children.only(null) : null;
|
|
151
|
+
});
|
|
152
|
+
SlotClone.displayName = `${ownerName}.SlotClone`;
|
|
153
|
+
return SlotClone;
|
|
154
|
+
}
|
|
155
|
+
var SLOTTABLE_IDENTIFIER = Symbol("radix.slottable");
|
|
156
|
+
function isSlottable(child) {
|
|
157
|
+
return React$1.isValidElement(child) && typeof child.type === "function" && "__radixId" in child.type && child.type.__radixId === SLOTTABLE_IDENTIFIER;
|
|
158
|
+
}
|
|
159
|
+
function mergeProps(slotProps, childProps) {
|
|
160
|
+
const overrideProps = { ...childProps };
|
|
161
|
+
for (const propName in childProps) {
|
|
162
|
+
const slotPropValue = slotProps[propName];
|
|
163
|
+
const childPropValue = childProps[propName];
|
|
164
|
+
if (/^on[A-Z]/.test(propName)) {
|
|
165
|
+
if (slotPropValue && childPropValue) overrideProps[propName] = (...args) => {
|
|
166
|
+
const result = childPropValue(...args);
|
|
167
|
+
slotPropValue(...args);
|
|
168
|
+
return result;
|
|
169
|
+
};
|
|
170
|
+
else if (slotPropValue) overrideProps[propName] = slotPropValue;
|
|
171
|
+
} else if (propName === "style") overrideProps[propName] = {
|
|
172
|
+
...slotPropValue,
|
|
173
|
+
...childPropValue
|
|
174
|
+
};
|
|
175
|
+
else if (propName === "className") overrideProps[propName] = [slotPropValue, childPropValue].filter(Boolean).join(" ");
|
|
176
|
+
}
|
|
177
|
+
return {
|
|
178
|
+
...slotProps,
|
|
179
|
+
...overrideProps
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
function getElementRef$1(element) {
|
|
183
|
+
let getter = Object.getOwnPropertyDescriptor(element.props, "ref")?.get;
|
|
184
|
+
let mayWarn = getter && "isReactWarning" in getter && getter.isReactWarning;
|
|
185
|
+
if (mayWarn) return element.ref;
|
|
186
|
+
getter = Object.getOwnPropertyDescriptor(element, "ref")?.get;
|
|
187
|
+
mayWarn = getter && "isReactWarning" in getter && getter.isReactWarning;
|
|
188
|
+
if (mayWarn) return element.props.ref;
|
|
189
|
+
return element.props.ref || element.ref;
|
|
190
|
+
}
|
|
191
|
+
//#endregion
|
|
192
|
+
//#region ../../node_modules/.pnpm/@radix-ui+react-collection@1.1.7_@types+react-dom@19.2.3_@types+react@19.2.7__@types+re_51f38897cc3aa0a11933866657bdcfb1/node_modules/@radix-ui/react-collection/dist/index.mjs
|
|
193
|
+
function createCollection(name) {
|
|
194
|
+
const PROVIDER_NAME = name + "CollectionProvider";
|
|
195
|
+
const [createCollectionContext, createCollectionScope] = createContextScope(PROVIDER_NAME);
|
|
196
|
+
const [CollectionProviderImpl, useCollectionContext] = createCollectionContext(PROVIDER_NAME, {
|
|
197
|
+
collectionRef: { current: null },
|
|
198
|
+
itemMap: /* @__PURE__ */ new Map()
|
|
199
|
+
});
|
|
200
|
+
const CollectionProvider = (props) => {
|
|
201
|
+
const { scope, children } = props;
|
|
202
|
+
const ref = React.useRef(null);
|
|
203
|
+
const itemMap = React.useRef(/* @__PURE__ */ new Map()).current;
|
|
204
|
+
return /* @__PURE__ */ jsx(CollectionProviderImpl, {
|
|
205
|
+
scope,
|
|
206
|
+
itemMap,
|
|
207
|
+
collectionRef: ref,
|
|
208
|
+
children
|
|
209
|
+
});
|
|
210
|
+
};
|
|
211
|
+
CollectionProvider.displayName = PROVIDER_NAME;
|
|
212
|
+
const COLLECTION_SLOT_NAME = name + "CollectionSlot";
|
|
213
|
+
const CollectionSlotImpl = /* @__PURE__ */ createSlot(COLLECTION_SLOT_NAME);
|
|
214
|
+
const CollectionSlot = React.forwardRef((props, forwardedRef) => {
|
|
215
|
+
const { scope, children } = props;
|
|
216
|
+
const composedRefs = useComposedRefs(forwardedRef, useCollectionContext(COLLECTION_SLOT_NAME, scope).collectionRef);
|
|
217
|
+
return /* @__PURE__ */ jsx(CollectionSlotImpl, {
|
|
218
|
+
ref: composedRefs,
|
|
219
|
+
children
|
|
220
|
+
});
|
|
221
|
+
});
|
|
222
|
+
CollectionSlot.displayName = COLLECTION_SLOT_NAME;
|
|
223
|
+
const ITEM_SLOT_NAME = name + "CollectionItemSlot";
|
|
224
|
+
const ITEM_DATA_ATTR = "data-radix-collection-item";
|
|
225
|
+
const CollectionItemSlotImpl = /* @__PURE__ */ createSlot(ITEM_SLOT_NAME);
|
|
226
|
+
const CollectionItemSlot = React.forwardRef((props, forwardedRef) => {
|
|
227
|
+
const { scope, children, ...itemData } = props;
|
|
228
|
+
const ref = React.useRef(null);
|
|
229
|
+
const composedRefs = useComposedRefs(forwardedRef, ref);
|
|
230
|
+
const context = useCollectionContext(ITEM_SLOT_NAME, scope);
|
|
231
|
+
React.useEffect(() => {
|
|
232
|
+
context.itemMap.set(ref, {
|
|
233
|
+
ref,
|
|
234
|
+
...itemData
|
|
235
|
+
});
|
|
236
|
+
return () => void context.itemMap.delete(ref);
|
|
237
|
+
});
|
|
238
|
+
return /* @__PURE__ */ jsx(CollectionItemSlotImpl, {
|
|
239
|
+
[ITEM_DATA_ATTR]: "",
|
|
240
|
+
ref: composedRefs,
|
|
241
|
+
children
|
|
242
|
+
});
|
|
243
|
+
});
|
|
244
|
+
CollectionItemSlot.displayName = ITEM_SLOT_NAME;
|
|
245
|
+
function useCollection(scope) {
|
|
246
|
+
const context = useCollectionContext(name + "CollectionConsumer", scope);
|
|
247
|
+
return React.useCallback(() => {
|
|
248
|
+
const collectionNode = context.collectionRef.current;
|
|
249
|
+
if (!collectionNode) return [];
|
|
250
|
+
const orderedNodes = Array.from(collectionNode.querySelectorAll(`[${ITEM_DATA_ATTR}]`));
|
|
251
|
+
return Array.from(context.itemMap.values()).sort((a, b) => orderedNodes.indexOf(a.ref.current) - orderedNodes.indexOf(b.ref.current));
|
|
252
|
+
}, [context.collectionRef, context.itemMap]);
|
|
253
|
+
}
|
|
254
|
+
return [
|
|
255
|
+
{
|
|
256
|
+
Provider: CollectionProvider,
|
|
257
|
+
Slot: CollectionSlot,
|
|
258
|
+
ItemSlot: CollectionItemSlot
|
|
259
|
+
},
|
|
260
|
+
useCollection,
|
|
261
|
+
createCollectionScope
|
|
262
|
+
];
|
|
263
|
+
}
|
|
264
|
+
//#endregion
|
|
265
|
+
//#region ../../node_modules/.pnpm/@radix-ui+react-use-layout-effect@1.1.1_@types+react@19.2.7_react@19.2.3/node_modules/@radix-ui/react-use-layout-effect/dist/index.mjs
|
|
266
|
+
var useLayoutEffect2 = globalThis?.document ? React$1.useLayoutEffect : () => {};
|
|
267
|
+
//#endregion
|
|
268
|
+
//#region ../../node_modules/.pnpm/@radix-ui+react-id@1.1.1_@types+react@19.2.7_react@19.2.3/node_modules/@radix-ui/react-id/dist/index.mjs
|
|
269
|
+
var useReactId = React$1[" useId ".trim().toString()] || (() => void 0);
|
|
270
|
+
var count = 0;
|
|
271
|
+
function useId$1(deterministicId) {
|
|
272
|
+
const [id, setId] = React$1.useState(useReactId());
|
|
273
|
+
useLayoutEffect2(() => {
|
|
274
|
+
if (!deterministicId) setId((reactId) => reactId ?? String(count++));
|
|
275
|
+
}, [deterministicId]);
|
|
276
|
+
return deterministicId || (id ? `radix-${id}` : "");
|
|
277
|
+
}
|
|
278
|
+
//#endregion
|
|
279
|
+
//#region ../../node_modules/.pnpm/@radix-ui+react-primitive@2.1.3_@types+react-dom@19.2.3_@types+react@19.2.7__@types+rea_5f3d28fea11cf58eed3830d0189b6e2d/node_modules/@radix-ui/react-primitive/dist/index.mjs
|
|
280
|
+
var Primitive = [
|
|
281
|
+
"a",
|
|
282
|
+
"button",
|
|
283
|
+
"div",
|
|
284
|
+
"form",
|
|
285
|
+
"h2",
|
|
286
|
+
"h3",
|
|
287
|
+
"img",
|
|
288
|
+
"input",
|
|
289
|
+
"label",
|
|
290
|
+
"li",
|
|
291
|
+
"nav",
|
|
292
|
+
"ol",
|
|
293
|
+
"p",
|
|
294
|
+
"select",
|
|
295
|
+
"span",
|
|
296
|
+
"svg",
|
|
297
|
+
"ul"
|
|
298
|
+
].reduce((primitive, node) => {
|
|
299
|
+
const Slot = /* @__PURE__ */ createSlot(`Primitive.${node}`);
|
|
300
|
+
const Node = React$1.forwardRef((props, forwardedRef) => {
|
|
301
|
+
const { asChild, ...primitiveProps } = props;
|
|
302
|
+
const Comp = asChild ? Slot : node;
|
|
303
|
+
if (typeof window !== "undefined") window[Symbol.for("radix-ui")] = true;
|
|
304
|
+
return /* @__PURE__ */ jsx(Comp, {
|
|
305
|
+
...primitiveProps,
|
|
306
|
+
ref: forwardedRef
|
|
307
|
+
});
|
|
308
|
+
});
|
|
309
|
+
Node.displayName = `Primitive.${node}`;
|
|
310
|
+
return {
|
|
311
|
+
...primitive,
|
|
312
|
+
[node]: Node
|
|
313
|
+
};
|
|
314
|
+
}, {});
|
|
315
|
+
//#endregion
|
|
316
|
+
//#region ../../node_modules/.pnpm/@radix-ui+react-use-callback-ref@1.1.1_@types+react@19.2.7_react@19.2.3/node_modules/@radix-ui/react-use-callback-ref/dist/index.mjs
|
|
317
|
+
function useCallbackRef(callback) {
|
|
318
|
+
const callbackRef = React$1.useRef(callback);
|
|
319
|
+
React$1.useEffect(() => {
|
|
320
|
+
callbackRef.current = callback;
|
|
321
|
+
});
|
|
322
|
+
return React$1.useMemo(() => (...args) => callbackRef.current?.(...args), []);
|
|
323
|
+
}
|
|
324
|
+
//#endregion
|
|
325
|
+
//#region ../../node_modules/.pnpm/@radix-ui+react-use-controllable-state@1.2.2_@types+react@19.2.7_react@19.2.3/node_modules/@radix-ui/react-use-controllable-state/dist/index.mjs
|
|
326
|
+
var useInsertionEffect = React$1[" useInsertionEffect ".trim().toString()] || useLayoutEffect2;
|
|
327
|
+
function useControllableState({ prop, defaultProp, onChange = () => {}, caller }) {
|
|
328
|
+
const [uncontrolledProp, setUncontrolledProp, onChangeRef] = useUncontrolledState({
|
|
329
|
+
defaultProp,
|
|
330
|
+
onChange
|
|
331
|
+
});
|
|
332
|
+
const isControlled = prop !== void 0;
|
|
333
|
+
const value = isControlled ? prop : uncontrolledProp;
|
|
334
|
+
{
|
|
335
|
+
const isControlledRef = React$1.useRef(prop !== void 0);
|
|
336
|
+
React$1.useEffect(() => {
|
|
337
|
+
const wasControlled = isControlledRef.current;
|
|
338
|
+
if (wasControlled !== isControlled) console.warn(`${caller} is changing from ${wasControlled ? "controlled" : "uncontrolled"} to ${isControlled ? "controlled" : "uncontrolled"}. Components should not switch from controlled to uncontrolled (or vice versa). Decide between using a controlled or uncontrolled value for the lifetime of the component.`);
|
|
339
|
+
isControlledRef.current = isControlled;
|
|
340
|
+
}, [isControlled, caller]);
|
|
341
|
+
}
|
|
342
|
+
return [value, React$1.useCallback((nextValue) => {
|
|
343
|
+
if (isControlled) {
|
|
344
|
+
const value2 = isFunction(nextValue) ? nextValue(prop) : nextValue;
|
|
345
|
+
if (value2 !== prop) onChangeRef.current?.(value2);
|
|
346
|
+
} else setUncontrolledProp(nextValue);
|
|
347
|
+
}, [
|
|
348
|
+
isControlled,
|
|
349
|
+
prop,
|
|
350
|
+
setUncontrolledProp,
|
|
351
|
+
onChangeRef
|
|
352
|
+
])];
|
|
353
|
+
}
|
|
354
|
+
function useUncontrolledState({ defaultProp, onChange }) {
|
|
355
|
+
const [value, setValue] = React$1.useState(defaultProp);
|
|
356
|
+
const prevValueRef = React$1.useRef(value);
|
|
357
|
+
const onChangeRef = React$1.useRef(onChange);
|
|
358
|
+
useInsertionEffect(() => {
|
|
359
|
+
onChangeRef.current = onChange;
|
|
360
|
+
}, [onChange]);
|
|
361
|
+
React$1.useEffect(() => {
|
|
362
|
+
if (prevValueRef.current !== value) {
|
|
363
|
+
onChangeRef.current?.(value);
|
|
364
|
+
prevValueRef.current = value;
|
|
365
|
+
}
|
|
366
|
+
}, [value, prevValueRef]);
|
|
367
|
+
return [
|
|
368
|
+
value,
|
|
369
|
+
setValue,
|
|
370
|
+
onChangeRef
|
|
371
|
+
];
|
|
372
|
+
}
|
|
373
|
+
function isFunction(value) {
|
|
374
|
+
return typeof value === "function";
|
|
375
|
+
}
|
|
376
|
+
//#endregion
|
|
377
|
+
//#region ../../node_modules/.pnpm/@radix-ui+react-direction@1.1.1_@types+react@19.2.7_react@19.2.3/node_modules/@radix-ui/react-direction/dist/index.mjs
|
|
378
|
+
var DirectionContext = React$1.createContext(void 0);
|
|
379
|
+
function useDirection(localDir) {
|
|
380
|
+
const globalDir = React$1.useContext(DirectionContext);
|
|
381
|
+
return localDir || globalDir || "ltr";
|
|
382
|
+
}
|
|
383
|
+
//#endregion
|
|
384
|
+
//#region ../../node_modules/.pnpm/@radix-ui+react-roving-focus@1.1.11_@types+react-dom@19.2.3_@types+react@19.2.7__@types_15baa560c953bc49315682e94c801f68/node_modules/@radix-ui/react-roving-focus/dist/index.mjs
|
|
385
|
+
var ENTRY_FOCUS = "rovingFocusGroup.onEntryFocus";
|
|
386
|
+
var EVENT_OPTIONS = {
|
|
387
|
+
bubbles: false,
|
|
388
|
+
cancelable: true
|
|
389
|
+
};
|
|
390
|
+
var GROUP_NAME = "RovingFocusGroup";
|
|
391
|
+
var [Collection, useCollection, createCollectionScope] = createCollection(GROUP_NAME);
|
|
392
|
+
var [createRovingFocusGroupContext, createRovingFocusGroupScope] = createContextScope(GROUP_NAME, [createCollectionScope]);
|
|
393
|
+
var [RovingFocusProvider, useRovingFocusContext] = createRovingFocusGroupContext(GROUP_NAME);
|
|
394
|
+
var RovingFocusGroup = React$1.forwardRef((props, forwardedRef) => {
|
|
395
|
+
return /* @__PURE__ */ jsx(Collection.Provider, {
|
|
396
|
+
scope: props.__scopeRovingFocusGroup,
|
|
397
|
+
children: /* @__PURE__ */ jsx(Collection.Slot, {
|
|
398
|
+
scope: props.__scopeRovingFocusGroup,
|
|
399
|
+
children: /* @__PURE__ */ jsx(RovingFocusGroupImpl, {
|
|
400
|
+
...props,
|
|
401
|
+
ref: forwardedRef
|
|
402
|
+
})
|
|
403
|
+
})
|
|
404
|
+
});
|
|
405
|
+
});
|
|
406
|
+
RovingFocusGroup.displayName = GROUP_NAME;
|
|
407
|
+
var RovingFocusGroupImpl = React$1.forwardRef((props, forwardedRef) => {
|
|
408
|
+
const { __scopeRovingFocusGroup, orientation, loop = false, dir, currentTabStopId: currentTabStopIdProp, defaultCurrentTabStopId, onCurrentTabStopIdChange, onEntryFocus, preventScrollOnEntryFocus = false, ...groupProps } = props;
|
|
409
|
+
const ref = React$1.useRef(null);
|
|
410
|
+
const composedRefs = useComposedRefs(forwardedRef, ref);
|
|
411
|
+
const direction = useDirection(dir);
|
|
412
|
+
const [currentTabStopId, setCurrentTabStopId] = useControllableState({
|
|
413
|
+
prop: currentTabStopIdProp,
|
|
414
|
+
defaultProp: defaultCurrentTabStopId ?? null,
|
|
415
|
+
onChange: onCurrentTabStopIdChange,
|
|
416
|
+
caller: GROUP_NAME
|
|
417
|
+
});
|
|
418
|
+
const [isTabbingBackOut, setIsTabbingBackOut] = React$1.useState(false);
|
|
419
|
+
const handleEntryFocus = useCallbackRef(onEntryFocus);
|
|
420
|
+
const getItems = useCollection(__scopeRovingFocusGroup);
|
|
421
|
+
const isClickFocusRef = React$1.useRef(false);
|
|
422
|
+
const [focusableItemsCount, setFocusableItemsCount] = React$1.useState(0);
|
|
423
|
+
React$1.useEffect(() => {
|
|
424
|
+
const node = ref.current;
|
|
425
|
+
if (node) {
|
|
426
|
+
node.addEventListener(ENTRY_FOCUS, handleEntryFocus);
|
|
427
|
+
return () => node.removeEventListener(ENTRY_FOCUS, handleEntryFocus);
|
|
428
|
+
}
|
|
429
|
+
}, [handleEntryFocus]);
|
|
430
|
+
return /* @__PURE__ */ jsx(RovingFocusProvider, {
|
|
431
|
+
scope: __scopeRovingFocusGroup,
|
|
432
|
+
orientation,
|
|
433
|
+
dir: direction,
|
|
434
|
+
loop,
|
|
435
|
+
currentTabStopId,
|
|
436
|
+
onItemFocus: React$1.useCallback((tabStopId) => setCurrentTabStopId(tabStopId), [setCurrentTabStopId]),
|
|
437
|
+
onItemShiftTab: React$1.useCallback(() => setIsTabbingBackOut(true), []),
|
|
438
|
+
onFocusableItemAdd: React$1.useCallback(() => setFocusableItemsCount((prevCount) => prevCount + 1), []),
|
|
439
|
+
onFocusableItemRemove: React$1.useCallback(() => setFocusableItemsCount((prevCount) => prevCount - 1), []),
|
|
440
|
+
children: /* @__PURE__ */ jsx(Primitive.div, {
|
|
441
|
+
tabIndex: isTabbingBackOut || focusableItemsCount === 0 ? -1 : 0,
|
|
442
|
+
"data-orientation": orientation,
|
|
443
|
+
...groupProps,
|
|
444
|
+
ref: composedRefs,
|
|
445
|
+
style: {
|
|
446
|
+
outline: "none",
|
|
447
|
+
...props.style
|
|
448
|
+
},
|
|
449
|
+
onMouseDown: composeEventHandlers(props.onMouseDown, () => {
|
|
450
|
+
isClickFocusRef.current = true;
|
|
451
|
+
}),
|
|
452
|
+
onFocus: composeEventHandlers(props.onFocus, (event) => {
|
|
453
|
+
const isKeyboardFocus = !isClickFocusRef.current;
|
|
454
|
+
if (event.target === event.currentTarget && isKeyboardFocus && !isTabbingBackOut) {
|
|
455
|
+
const entryFocusEvent = new CustomEvent(ENTRY_FOCUS, EVENT_OPTIONS);
|
|
456
|
+
event.currentTarget.dispatchEvent(entryFocusEvent);
|
|
457
|
+
if (!entryFocusEvent.defaultPrevented) {
|
|
458
|
+
const items = getItems().filter((item) => item.focusable);
|
|
459
|
+
focusFirst([
|
|
460
|
+
items.find((item) => item.active),
|
|
461
|
+
items.find((item) => item.id === currentTabStopId),
|
|
462
|
+
...items
|
|
463
|
+
].filter(Boolean).map((item) => item.ref.current), preventScrollOnEntryFocus);
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
isClickFocusRef.current = false;
|
|
467
|
+
}),
|
|
468
|
+
onBlur: composeEventHandlers(props.onBlur, () => setIsTabbingBackOut(false))
|
|
469
|
+
})
|
|
470
|
+
});
|
|
471
|
+
});
|
|
472
|
+
var ITEM_NAME = "RovingFocusGroupItem";
|
|
473
|
+
var RovingFocusGroupItem = React$1.forwardRef((props, forwardedRef) => {
|
|
474
|
+
const { __scopeRovingFocusGroup, focusable = true, active = false, tabStopId, children, ...itemProps } = props;
|
|
475
|
+
const autoId = useId$1();
|
|
476
|
+
const id = tabStopId || autoId;
|
|
477
|
+
const context = useRovingFocusContext(ITEM_NAME, __scopeRovingFocusGroup);
|
|
478
|
+
const isCurrentTabStop = context.currentTabStopId === id;
|
|
479
|
+
const getItems = useCollection(__scopeRovingFocusGroup);
|
|
480
|
+
const { onFocusableItemAdd, onFocusableItemRemove, currentTabStopId } = context;
|
|
481
|
+
React$1.useEffect(() => {
|
|
482
|
+
if (focusable) {
|
|
483
|
+
onFocusableItemAdd();
|
|
484
|
+
return () => onFocusableItemRemove();
|
|
485
|
+
}
|
|
486
|
+
}, [
|
|
487
|
+
focusable,
|
|
488
|
+
onFocusableItemAdd,
|
|
489
|
+
onFocusableItemRemove
|
|
490
|
+
]);
|
|
491
|
+
return /* @__PURE__ */ jsx(Collection.ItemSlot, {
|
|
492
|
+
scope: __scopeRovingFocusGroup,
|
|
493
|
+
id,
|
|
494
|
+
focusable,
|
|
495
|
+
active,
|
|
496
|
+
children: /* @__PURE__ */ jsx(Primitive.span, {
|
|
497
|
+
tabIndex: isCurrentTabStop ? 0 : -1,
|
|
498
|
+
"data-orientation": context.orientation,
|
|
499
|
+
...itemProps,
|
|
500
|
+
ref: forwardedRef,
|
|
501
|
+
onMouseDown: composeEventHandlers(props.onMouseDown, (event) => {
|
|
502
|
+
if (!focusable) event.preventDefault();
|
|
503
|
+
else context.onItemFocus(id);
|
|
504
|
+
}),
|
|
505
|
+
onFocus: composeEventHandlers(props.onFocus, () => context.onItemFocus(id)),
|
|
506
|
+
onKeyDown: composeEventHandlers(props.onKeyDown, (event) => {
|
|
507
|
+
if (event.key === "Tab" && event.shiftKey) {
|
|
508
|
+
context.onItemShiftTab();
|
|
509
|
+
return;
|
|
510
|
+
}
|
|
511
|
+
if (event.target !== event.currentTarget) return;
|
|
512
|
+
const focusIntent = getFocusIntent(event, context.orientation, context.dir);
|
|
513
|
+
if (focusIntent !== void 0) {
|
|
514
|
+
if (event.metaKey || event.ctrlKey || event.altKey || event.shiftKey) return;
|
|
515
|
+
event.preventDefault();
|
|
516
|
+
let candidateNodes = getItems().filter((item) => item.focusable).map((item) => item.ref.current);
|
|
517
|
+
if (focusIntent === "last") candidateNodes.reverse();
|
|
518
|
+
else if (focusIntent === "prev" || focusIntent === "next") {
|
|
519
|
+
if (focusIntent === "prev") candidateNodes.reverse();
|
|
520
|
+
const currentIndex = candidateNodes.indexOf(event.currentTarget);
|
|
521
|
+
candidateNodes = context.loop ? wrapArray(candidateNodes, currentIndex + 1) : candidateNodes.slice(currentIndex + 1);
|
|
522
|
+
}
|
|
523
|
+
setTimeout(() => focusFirst(candidateNodes));
|
|
524
|
+
}
|
|
525
|
+
}),
|
|
526
|
+
children: typeof children === "function" ? children({
|
|
527
|
+
isCurrentTabStop,
|
|
528
|
+
hasTabStop: currentTabStopId != null
|
|
529
|
+
}) : children
|
|
530
|
+
})
|
|
531
|
+
});
|
|
532
|
+
});
|
|
533
|
+
RovingFocusGroupItem.displayName = ITEM_NAME;
|
|
534
|
+
var MAP_KEY_TO_FOCUS_INTENT = {
|
|
535
|
+
ArrowLeft: "prev",
|
|
536
|
+
ArrowUp: "prev",
|
|
537
|
+
ArrowRight: "next",
|
|
538
|
+
ArrowDown: "next",
|
|
539
|
+
PageUp: "first",
|
|
540
|
+
Home: "first",
|
|
541
|
+
PageDown: "last",
|
|
542
|
+
End: "last"
|
|
543
|
+
};
|
|
544
|
+
function getDirectionAwareKey(key, dir) {
|
|
545
|
+
if (dir !== "rtl") return key;
|
|
546
|
+
return key === "ArrowLeft" ? "ArrowRight" : key === "ArrowRight" ? "ArrowLeft" : key;
|
|
547
|
+
}
|
|
548
|
+
function getFocusIntent(event, orientation, dir) {
|
|
549
|
+
const key = getDirectionAwareKey(event.key, dir);
|
|
550
|
+
if (orientation === "vertical" && ["ArrowLeft", "ArrowRight"].includes(key)) return void 0;
|
|
551
|
+
if (orientation === "horizontal" && ["ArrowUp", "ArrowDown"].includes(key)) return void 0;
|
|
552
|
+
return MAP_KEY_TO_FOCUS_INTENT[key];
|
|
553
|
+
}
|
|
554
|
+
function focusFirst(candidates, preventScroll = false) {
|
|
555
|
+
const PREVIOUSLY_FOCUSED_ELEMENT = document.activeElement;
|
|
556
|
+
for (const candidate of candidates) {
|
|
557
|
+
if (candidate === PREVIOUSLY_FOCUSED_ELEMENT) return;
|
|
558
|
+
candidate.focus({ preventScroll });
|
|
559
|
+
if (document.activeElement !== PREVIOUSLY_FOCUSED_ELEMENT) return;
|
|
560
|
+
}
|
|
561
|
+
}
|
|
562
|
+
function wrapArray(array, startIndex) {
|
|
563
|
+
return array.map((_, index) => array[(startIndex + index) % array.length]);
|
|
564
|
+
}
|
|
565
|
+
var Root = RovingFocusGroup;
|
|
566
|
+
var Item = RovingFocusGroupItem;
|
|
567
|
+
//#endregion
|
|
568
|
+
//#region ../../node_modules/.pnpm/@radix-ui+react-presence@1.1.5_@types+react-dom@19.2.3_@types+react@19.2.7__@types+reac_90b29b5754215655309320557426a63a/node_modules/@radix-ui/react-presence/dist/index.mjs
|
|
569
|
+
function useStateMachine(initialState, machine) {
|
|
570
|
+
return React$1.useReducer((state, event) => {
|
|
571
|
+
return machine[state][event] ?? state;
|
|
572
|
+
}, initialState);
|
|
573
|
+
}
|
|
574
|
+
var Presence = (props) => {
|
|
575
|
+
const { present, children } = props;
|
|
576
|
+
const presence = usePresence(present);
|
|
577
|
+
const child = typeof children === "function" ? children({ present: presence.isPresent }) : React$1.Children.only(children);
|
|
578
|
+
const ref = useComposedRefs(presence.ref, getElementRef(child));
|
|
579
|
+
return typeof children === "function" || presence.isPresent ? React$1.cloneElement(child, { ref }) : null;
|
|
580
|
+
};
|
|
581
|
+
Presence.displayName = "Presence";
|
|
582
|
+
function usePresence(present) {
|
|
583
|
+
const [node, setNode] = React$1.useState();
|
|
584
|
+
const stylesRef = React$1.useRef(null);
|
|
585
|
+
const prevPresentRef = React$1.useRef(present);
|
|
586
|
+
const prevAnimationNameRef = React$1.useRef("none");
|
|
587
|
+
const [state, send] = useStateMachine(present ? "mounted" : "unmounted", {
|
|
588
|
+
mounted: {
|
|
589
|
+
UNMOUNT: "unmounted",
|
|
590
|
+
ANIMATION_OUT: "unmountSuspended"
|
|
591
|
+
},
|
|
592
|
+
unmountSuspended: {
|
|
593
|
+
MOUNT: "mounted",
|
|
594
|
+
ANIMATION_END: "unmounted"
|
|
595
|
+
},
|
|
596
|
+
unmounted: { MOUNT: "mounted" }
|
|
597
|
+
});
|
|
598
|
+
React$1.useEffect(() => {
|
|
599
|
+
const currentAnimationName = getAnimationName(stylesRef.current);
|
|
600
|
+
prevAnimationNameRef.current = state === "mounted" ? currentAnimationName : "none";
|
|
601
|
+
}, [state]);
|
|
602
|
+
useLayoutEffect2(() => {
|
|
603
|
+
const styles = stylesRef.current;
|
|
604
|
+
const wasPresent = prevPresentRef.current;
|
|
605
|
+
if (wasPresent !== present) {
|
|
606
|
+
const prevAnimationName = prevAnimationNameRef.current;
|
|
607
|
+
const currentAnimationName = getAnimationName(styles);
|
|
608
|
+
if (present) send("MOUNT");
|
|
609
|
+
else if (currentAnimationName === "none" || styles?.display === "none") send("UNMOUNT");
|
|
610
|
+
else if (wasPresent && prevAnimationName !== currentAnimationName) send("ANIMATION_OUT");
|
|
611
|
+
else send("UNMOUNT");
|
|
612
|
+
prevPresentRef.current = present;
|
|
613
|
+
}
|
|
614
|
+
}, [present, send]);
|
|
615
|
+
useLayoutEffect2(() => {
|
|
616
|
+
if (node) {
|
|
617
|
+
let timeoutId;
|
|
618
|
+
const ownerWindow = node.ownerDocument.defaultView ?? window;
|
|
619
|
+
const handleAnimationEnd = (event) => {
|
|
620
|
+
const isCurrentAnimation = getAnimationName(stylesRef.current).includes(CSS.escape(event.animationName));
|
|
621
|
+
if (event.target === node && isCurrentAnimation) {
|
|
622
|
+
send("ANIMATION_END");
|
|
623
|
+
if (!prevPresentRef.current) {
|
|
624
|
+
const currentFillMode = node.style.animationFillMode;
|
|
625
|
+
node.style.animationFillMode = "forwards";
|
|
626
|
+
timeoutId = ownerWindow.setTimeout(() => {
|
|
627
|
+
if (node.style.animationFillMode === "forwards") node.style.animationFillMode = currentFillMode;
|
|
628
|
+
});
|
|
629
|
+
}
|
|
630
|
+
}
|
|
631
|
+
};
|
|
632
|
+
const handleAnimationStart = (event) => {
|
|
633
|
+
if (event.target === node) prevAnimationNameRef.current = getAnimationName(stylesRef.current);
|
|
634
|
+
};
|
|
635
|
+
node.addEventListener("animationstart", handleAnimationStart);
|
|
636
|
+
node.addEventListener("animationcancel", handleAnimationEnd);
|
|
637
|
+
node.addEventListener("animationend", handleAnimationEnd);
|
|
638
|
+
return () => {
|
|
639
|
+
ownerWindow.clearTimeout(timeoutId);
|
|
640
|
+
node.removeEventListener("animationstart", handleAnimationStart);
|
|
641
|
+
node.removeEventListener("animationcancel", handleAnimationEnd);
|
|
642
|
+
node.removeEventListener("animationend", handleAnimationEnd);
|
|
643
|
+
};
|
|
644
|
+
} else send("ANIMATION_END");
|
|
645
|
+
}, [node, send]);
|
|
646
|
+
return {
|
|
647
|
+
isPresent: ["mounted", "unmountSuspended"].includes(state),
|
|
648
|
+
ref: React$1.useCallback((node2) => {
|
|
649
|
+
stylesRef.current = node2 ? getComputedStyle(node2) : null;
|
|
650
|
+
setNode(node2);
|
|
651
|
+
}, [])
|
|
652
|
+
};
|
|
653
|
+
}
|
|
654
|
+
function getAnimationName(styles) {
|
|
655
|
+
return styles?.animationName || "none";
|
|
656
|
+
}
|
|
657
|
+
function getElementRef(element) {
|
|
658
|
+
let getter = Object.getOwnPropertyDescriptor(element.props, "ref")?.get;
|
|
659
|
+
let mayWarn = getter && "isReactWarning" in getter && getter.isReactWarning;
|
|
660
|
+
if (mayWarn) return element.ref;
|
|
661
|
+
getter = Object.getOwnPropertyDescriptor(element, "ref")?.get;
|
|
662
|
+
mayWarn = getter && "isReactWarning" in getter && getter.isReactWarning;
|
|
663
|
+
if (mayWarn) return element.props.ref;
|
|
664
|
+
return element.props.ref || element.ref;
|
|
665
|
+
}
|
|
666
|
+
//#endregion
|
|
667
|
+
//#region ../../node_modules/.pnpm/@radix-ui+react-tabs@1.1.13_@types+react-dom@19.2.3_@types+react@19.2.7__@types+react@1_dc861e27c2a200eb839e0d3bcc9fb0d9/node_modules/@radix-ui/react-tabs/dist/index.mjs
|
|
668
|
+
var TABS_NAME = "Tabs";
|
|
669
|
+
var [createTabsContext, createTabsScope] = createContextScope(TABS_NAME, [createRovingFocusGroupScope]);
|
|
670
|
+
var useRovingFocusGroupScope = createRovingFocusGroupScope();
|
|
671
|
+
var [TabsProvider, useTabsContext] = createTabsContext(TABS_NAME);
|
|
672
|
+
var Tabs$1 = React$1.forwardRef((props, forwardedRef) => {
|
|
673
|
+
const { __scopeTabs, value: valueProp, onValueChange, defaultValue, orientation = "horizontal", dir, activationMode = "automatic", ...tabsProps } = props;
|
|
674
|
+
const direction = useDirection(dir);
|
|
675
|
+
const [value, setValue] = useControllableState({
|
|
676
|
+
prop: valueProp,
|
|
677
|
+
onChange: onValueChange,
|
|
678
|
+
defaultProp: defaultValue ?? "",
|
|
679
|
+
caller: TABS_NAME
|
|
680
|
+
});
|
|
681
|
+
return /* @__PURE__ */ jsx(TabsProvider, {
|
|
682
|
+
scope: __scopeTabs,
|
|
683
|
+
baseId: useId$1(),
|
|
684
|
+
value,
|
|
685
|
+
onValueChange: setValue,
|
|
686
|
+
orientation,
|
|
687
|
+
dir: direction,
|
|
688
|
+
activationMode,
|
|
689
|
+
children: /* @__PURE__ */ jsx(Primitive.div, {
|
|
690
|
+
dir: direction,
|
|
691
|
+
"data-orientation": orientation,
|
|
692
|
+
...tabsProps,
|
|
693
|
+
ref: forwardedRef
|
|
694
|
+
})
|
|
695
|
+
});
|
|
696
|
+
});
|
|
697
|
+
Tabs$1.displayName = TABS_NAME;
|
|
698
|
+
var TAB_LIST_NAME = "TabsList";
|
|
699
|
+
var TabsList$1 = React$1.forwardRef((props, forwardedRef) => {
|
|
700
|
+
const { __scopeTabs, loop = true, ...listProps } = props;
|
|
701
|
+
const context = useTabsContext(TAB_LIST_NAME, __scopeTabs);
|
|
702
|
+
const rovingFocusGroupScope = useRovingFocusGroupScope(__scopeTabs);
|
|
703
|
+
return /* @__PURE__ */ jsx(Root, {
|
|
704
|
+
asChild: true,
|
|
705
|
+
...rovingFocusGroupScope,
|
|
706
|
+
orientation: context.orientation,
|
|
707
|
+
dir: context.dir,
|
|
708
|
+
loop,
|
|
709
|
+
children: /* @__PURE__ */ jsx(Primitive.div, {
|
|
710
|
+
role: "tablist",
|
|
711
|
+
"aria-orientation": context.orientation,
|
|
712
|
+
...listProps,
|
|
713
|
+
ref: forwardedRef
|
|
714
|
+
})
|
|
715
|
+
});
|
|
716
|
+
});
|
|
717
|
+
TabsList$1.displayName = TAB_LIST_NAME;
|
|
718
|
+
var TRIGGER_NAME = "TabsTrigger";
|
|
719
|
+
var TabsTrigger$1 = React$1.forwardRef((props, forwardedRef) => {
|
|
720
|
+
const { __scopeTabs, value, disabled = false, ...triggerProps } = props;
|
|
721
|
+
const context = useTabsContext(TRIGGER_NAME, __scopeTabs);
|
|
722
|
+
const rovingFocusGroupScope = useRovingFocusGroupScope(__scopeTabs);
|
|
723
|
+
const triggerId = makeTriggerId(context.baseId, value);
|
|
724
|
+
const contentId = makeContentId(context.baseId, value);
|
|
725
|
+
const isSelected = value === context.value;
|
|
726
|
+
return /* @__PURE__ */ jsx(Item, {
|
|
727
|
+
asChild: true,
|
|
728
|
+
...rovingFocusGroupScope,
|
|
729
|
+
focusable: !disabled,
|
|
730
|
+
active: isSelected,
|
|
731
|
+
children: /* @__PURE__ */ jsx(Primitive.button, {
|
|
732
|
+
type: "button",
|
|
733
|
+
role: "tab",
|
|
734
|
+
"aria-selected": isSelected,
|
|
735
|
+
"aria-controls": contentId,
|
|
736
|
+
"data-state": isSelected ? "active" : "inactive",
|
|
737
|
+
"data-disabled": disabled ? "" : void 0,
|
|
738
|
+
disabled,
|
|
739
|
+
id: triggerId,
|
|
740
|
+
...triggerProps,
|
|
741
|
+
ref: forwardedRef,
|
|
742
|
+
onMouseDown: composeEventHandlers(props.onMouseDown, (event) => {
|
|
743
|
+
if (!disabled && event.button === 0 && event.ctrlKey === false) context.onValueChange(value);
|
|
744
|
+
else event.preventDefault();
|
|
745
|
+
}),
|
|
746
|
+
onKeyDown: composeEventHandlers(props.onKeyDown, (event) => {
|
|
747
|
+
if ([" ", "Enter"].includes(event.key)) context.onValueChange(value);
|
|
748
|
+
}),
|
|
749
|
+
onFocus: composeEventHandlers(props.onFocus, () => {
|
|
750
|
+
const isAutomaticActivation = context.activationMode !== "manual";
|
|
751
|
+
if (!isSelected && !disabled && isAutomaticActivation) context.onValueChange(value);
|
|
752
|
+
})
|
|
753
|
+
})
|
|
754
|
+
});
|
|
755
|
+
});
|
|
756
|
+
TabsTrigger$1.displayName = TRIGGER_NAME;
|
|
757
|
+
var CONTENT_NAME = "TabsContent";
|
|
758
|
+
var TabsContent = React$1.forwardRef((props, forwardedRef) => {
|
|
759
|
+
const { __scopeTabs, value, forceMount, children, ...contentProps } = props;
|
|
760
|
+
const context = useTabsContext(CONTENT_NAME, __scopeTabs);
|
|
761
|
+
const triggerId = makeTriggerId(context.baseId, value);
|
|
762
|
+
const contentId = makeContentId(context.baseId, value);
|
|
763
|
+
const isSelected = value === context.value;
|
|
764
|
+
const isMountAnimationPreventedRef = React$1.useRef(isSelected);
|
|
765
|
+
React$1.useEffect(() => {
|
|
766
|
+
const rAF = requestAnimationFrame(() => isMountAnimationPreventedRef.current = false);
|
|
767
|
+
return () => cancelAnimationFrame(rAF);
|
|
768
|
+
}, []);
|
|
769
|
+
return /* @__PURE__ */ jsx(Presence, {
|
|
770
|
+
present: forceMount || isSelected,
|
|
771
|
+
children: ({ present }) => /* @__PURE__ */ jsx(Primitive.div, {
|
|
772
|
+
"data-state": isSelected ? "active" : "inactive",
|
|
773
|
+
"data-orientation": context.orientation,
|
|
774
|
+
role: "tabpanel",
|
|
775
|
+
"aria-labelledby": triggerId,
|
|
776
|
+
hidden: !present,
|
|
777
|
+
id: contentId,
|
|
778
|
+
tabIndex: 0,
|
|
779
|
+
...contentProps,
|
|
780
|
+
ref: forwardedRef,
|
|
781
|
+
style: {
|
|
782
|
+
...props.style,
|
|
783
|
+
animationDuration: isMountAnimationPreventedRef.current ? "0s" : void 0
|
|
784
|
+
},
|
|
785
|
+
children: present && children
|
|
786
|
+
})
|
|
787
|
+
});
|
|
788
|
+
});
|
|
789
|
+
TabsContent.displayName = CONTENT_NAME;
|
|
790
|
+
function makeTriggerId(baseId, value) {
|
|
791
|
+
return `${baseId}-trigger-${value}`;
|
|
792
|
+
}
|
|
793
|
+
function makeContentId(baseId, value) {
|
|
794
|
+
return `${baseId}-content-${value}`;
|
|
795
|
+
}
|
|
796
|
+
var Root2 = Tabs$1;
|
|
797
|
+
var List = TabsList$1;
|
|
798
|
+
var Trigger = TabsTrigger$1;
|
|
799
|
+
//#endregion
|
|
800
|
+
//#region ../ui/src/components/primitives/tabs.tsx
|
|
801
|
+
var TabsContext = React$1.createContext({
|
|
802
|
+
variant: "default",
|
|
803
|
+
orientation: "horizontal"
|
|
804
|
+
});
|
|
805
|
+
function Tabs({ className, variant = "default", orientation = "horizontal", ...props }) {
|
|
806
|
+
return /* @__PURE__ */ jsx(TabsContext, {
|
|
807
|
+
value: {
|
|
808
|
+
variant,
|
|
809
|
+
orientation
|
|
810
|
+
},
|
|
811
|
+
children: /* @__PURE__ */ jsx(Root2, {
|
|
812
|
+
"data-slot": "tabs",
|
|
813
|
+
orientation,
|
|
814
|
+
className: cn("flex flex-col gap-2", orientation === "vertical" && "flex-row", className),
|
|
815
|
+
...props
|
|
816
|
+
})
|
|
817
|
+
});
|
|
818
|
+
}
|
|
819
|
+
var tabsListVariants = cva("inline-flex items-center justify-center", {
|
|
820
|
+
variants: {
|
|
821
|
+
variant: {
|
|
822
|
+
default: "h-9 w-fit rounded-lg bg-muted p-[3px] text-muted-foreground",
|
|
823
|
+
line: "justify-start gap-4 border-b-0 bg-transparent p-0",
|
|
824
|
+
underline: "justify-start gap-0 border-b-0 bg-transparent p-0",
|
|
825
|
+
workflow: "justify-start gap-3 border-b-0 bg-transparent p-0"
|
|
826
|
+
},
|
|
827
|
+
orientation: {
|
|
828
|
+
horizontal: "flex-row",
|
|
829
|
+
vertical: "h-fit flex-col"
|
|
830
|
+
}
|
|
831
|
+
},
|
|
832
|
+
compoundVariants: [{
|
|
833
|
+
variant: "default",
|
|
834
|
+
orientation: "vertical",
|
|
835
|
+
class: "h-fit w-fit flex-col"
|
|
836
|
+
}, {
|
|
837
|
+
variant: "line",
|
|
838
|
+
orientation: "vertical",
|
|
839
|
+
class: "flex-col items-stretch pb-0"
|
|
840
|
+
}],
|
|
841
|
+
defaultVariants: {
|
|
842
|
+
variant: "default",
|
|
843
|
+
orientation: "horizontal"
|
|
844
|
+
}
|
|
845
|
+
});
|
|
846
|
+
function TabsList({ className, ...props }) {
|
|
847
|
+
const { variant, orientation } = React$1.use(TabsContext);
|
|
848
|
+
return /* @__PURE__ */ jsx(List, {
|
|
849
|
+
"data-slot": "tabs-list",
|
|
850
|
+
className: cn(tabsListVariants({
|
|
851
|
+
variant,
|
|
852
|
+
orientation
|
|
853
|
+
}), className),
|
|
854
|
+
...props
|
|
855
|
+
});
|
|
856
|
+
}
|
|
857
|
+
var tabsTriggerVariants = cva([
|
|
858
|
+
"inline-flex items-center justify-center text-sm font-medium whitespace-nowrap",
|
|
859
|
+
"disabled:pointer-events-none disabled:opacity-50",
|
|
860
|
+
"transition-all outline-none",
|
|
861
|
+
"[&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*=\"size-\"])]:size-4"
|
|
862
|
+
], {
|
|
863
|
+
variants: {
|
|
864
|
+
variant: {
|
|
865
|
+
default: [
|
|
866
|
+
"h-[calc(100%-1px)] flex-1 gap-1.5 rounded-md px-2 py-1",
|
|
867
|
+
"border border-transparent text-foreground",
|
|
868
|
+
"dark:text-muted-foreground",
|
|
869
|
+
"focus-visible:border-primary focus-visible:bg-accent",
|
|
870
|
+
"data-[state=active]:bg-background data-[state=active]:shadow-sm",
|
|
871
|
+
"dark:data-[state=active]:border-input dark:data-[state=active]:bg-input/30 dark:data-[state=active]:text-foreground"
|
|
872
|
+
],
|
|
873
|
+
line: [
|
|
874
|
+
"relative gap-2 px-2 py-2",
|
|
875
|
+
"font-normal text-muted-foreground hover:text-foreground",
|
|
876
|
+
"focus-visible:text-foreground focus-visible:underline focus-visible:underline-offset-4",
|
|
877
|
+
"data-[state=active]:text-primary",
|
|
878
|
+
"after:absolute after:rounded-full after:bg-transparent",
|
|
879
|
+
"data-[state=active]:after:bg-primary"
|
|
880
|
+
],
|
|
881
|
+
underline: [
|
|
882
|
+
"relative gap-1.5 px-2.5 py-2",
|
|
883
|
+
"font-normal text-muted-foreground hover:text-foreground",
|
|
884
|
+
"focus-visible:text-foreground focus-visible:underline focus-visible:underline-offset-4",
|
|
885
|
+
"data-[state=active]:text-foreground",
|
|
886
|
+
"after:absolute after:rounded-none after:bg-transparent",
|
|
887
|
+
"data-[state=active]:after:bg-primary"
|
|
888
|
+
],
|
|
889
|
+
workflow: [
|
|
890
|
+
"relative gap-1.5 px-1 py-1.5 text-sm font-normal",
|
|
891
|
+
"text-muted-foreground hover:text-foreground",
|
|
892
|
+
"rounded-sm focus-visible:text-foreground focus-visible:underline focus-visible:underline-offset-4",
|
|
893
|
+
"data-[state=active]:font-semibold data-[state=active]:text-foreground",
|
|
894
|
+
"data-[state=active]:underline data-[state=active]:decoration-1 data-[state=active]:underline-offset-4",
|
|
895
|
+
"[&:not(:first-child)]:before:content-['›']",
|
|
896
|
+
"[&:not(:first-child)]:before:mr-3 [&:not(:first-child)]:before:text-base",
|
|
897
|
+
"[&:not(:first-child)]:before:font-normal [&:not(:first-child)]:before:no-underline",
|
|
898
|
+
"[&:not(:first-child)]:before:text-muted-foreground"
|
|
899
|
+
]
|
|
900
|
+
},
|
|
901
|
+
orientation: {
|
|
902
|
+
horizontal: "",
|
|
903
|
+
vertical: "rounded-full"
|
|
904
|
+
}
|
|
905
|
+
},
|
|
906
|
+
compoundVariants: [
|
|
907
|
+
{
|
|
908
|
+
variant: "line",
|
|
909
|
+
orientation: "horizontal",
|
|
910
|
+
class: "after:bottom-0 after:left-0 after:h-[2px] after:w-full data-[state=active]:after:h-[4px]"
|
|
911
|
+
},
|
|
912
|
+
{
|
|
913
|
+
variant: "line",
|
|
914
|
+
orientation: "vertical",
|
|
915
|
+
class: ["justify-center after:bottom-0 after:left-0 after:h-[4px] after:w-full after:bg-transparent data-[state=active]:after:bg-primary", "hover:bg-primary/10 hover:text-primary"]
|
|
916
|
+
},
|
|
917
|
+
{
|
|
918
|
+
variant: "underline",
|
|
919
|
+
orientation: "horizontal",
|
|
920
|
+
class: "after:bottom-0 after:left-0 after:h-0.5 after:w-full"
|
|
921
|
+
}
|
|
922
|
+
],
|
|
923
|
+
defaultVariants: {
|
|
924
|
+
variant: "default",
|
|
925
|
+
orientation: "horizontal"
|
|
926
|
+
}
|
|
927
|
+
});
|
|
928
|
+
function TabsTrigger({ className, ...props }) {
|
|
929
|
+
const { variant, orientation } = React$1.use(TabsContext);
|
|
930
|
+
return /* @__PURE__ */ jsx(Trigger, {
|
|
931
|
+
"data-slot": "tabs-trigger",
|
|
932
|
+
className: cn(tabsTriggerVariants({
|
|
933
|
+
variant,
|
|
934
|
+
orientation
|
|
935
|
+
}), className),
|
|
936
|
+
...props
|
|
937
|
+
});
|
|
938
|
+
}
|
|
939
|
+
//#endregion
|
|
940
|
+
//#region src/plugins/spreadsheet/SpreadsheetFilePreviewToolbar.tsx
|
|
941
|
+
function SpreadsheetFilePreviewToolbar({ canZoomIn, canZoomOut, onZoomIn, onZoomOut, zoomLabel }) {
|
|
942
|
+
const { t } = useTranslation();
|
|
943
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
944
|
+
role: "toolbar",
|
|
945
|
+
"aria-label": t("preview.label"),
|
|
946
|
+
className: "flex shrink-0 items-center gap-1",
|
|
947
|
+
children: [
|
|
948
|
+
/* @__PURE__ */ jsx(FilePreviewToolbarButton, {
|
|
949
|
+
label: t("preview.zoom_out"),
|
|
950
|
+
disabled: !canZoomOut,
|
|
951
|
+
onClick: onZoomOut,
|
|
952
|
+
children: /* @__PURE__ */ jsx(ZoomOut, { "aria-hidden": true })
|
|
953
|
+
}),
|
|
954
|
+
/* @__PURE__ */ jsx("span", {
|
|
955
|
+
className: "min-w-12 px-1 text-center text-muted-foreground text-xs tabular-nums",
|
|
956
|
+
"data-testid": "xlsx-preview-zoom-value",
|
|
957
|
+
children: zoomLabel
|
|
958
|
+
}),
|
|
959
|
+
/* @__PURE__ */ jsx(FilePreviewToolbarButton, {
|
|
960
|
+
label: t("preview.zoom_in"),
|
|
961
|
+
disabled: !canZoomIn,
|
|
962
|
+
onClick: onZoomIn,
|
|
963
|
+
children: /* @__PURE__ */ jsx(ZoomIn, { "aria-hidden": true })
|
|
964
|
+
})
|
|
965
|
+
]
|
|
966
|
+
});
|
|
967
|
+
}
|
|
968
|
+
//#endregion
|
|
969
|
+
//#region src/plugins/spreadsheet/useXlsxWorkbook.ts
|
|
970
|
+
/** Files above this size are not parsed and fall back to opening in an external app. */
|
|
971
|
+
var XLSX_PREVIEW_MAX_SIZE_BYTES = 20971520;
|
|
972
|
+
function useXlsxWorkbook(document, fileName) {
|
|
973
|
+
const logger = usePreviewLogger("useXlsxWorkbook");
|
|
974
|
+
const { failDocument, resources } = usePreviewHost();
|
|
975
|
+
const { baseUrl, createWorker } = resources ?? {};
|
|
976
|
+
const [state, setState] = useState({ status: "idle" });
|
|
977
|
+
const workerRef = useRef(null);
|
|
978
|
+
const requestIdRef = useRef(0);
|
|
979
|
+
const loggedWarningsRef = useRef(/* @__PURE__ */ new Set());
|
|
980
|
+
useEffect(() => {
|
|
981
|
+
let cancelled = false;
|
|
982
|
+
const controller = new AbortController();
|
|
983
|
+
const requestId = ++requestIdRef.current;
|
|
984
|
+
if (document.size > 20971520) {
|
|
985
|
+
failDocument?.(new PreviewError("too_large", "Workbook exceeds the preview size limit"));
|
|
986
|
+
setState({
|
|
987
|
+
status: "oversize",
|
|
988
|
+
sizeBytes: document.size
|
|
989
|
+
});
|
|
990
|
+
return;
|
|
991
|
+
}
|
|
992
|
+
setState({ status: "loading" });
|
|
993
|
+
(async () => {
|
|
994
|
+
let bytes;
|
|
995
|
+
try {
|
|
996
|
+
const raw = await readPreviewDocument(document, XLSX_PREVIEW_MAX_SIZE_BYTES, controller.signal);
|
|
997
|
+
if (cancelled || requestId !== requestIdRef.current) return;
|
|
998
|
+
bytes = raw.slice().buffer;
|
|
999
|
+
} catch (error) {
|
|
1000
|
+
if (cancelled || requestId !== requestIdRef.current) return;
|
|
1001
|
+
const normalized = error instanceof Error ? error : new Error(String(error));
|
|
1002
|
+
failDocument?.(normalized);
|
|
1003
|
+
setState({
|
|
1004
|
+
status: "error",
|
|
1005
|
+
message: normalized.message
|
|
1006
|
+
});
|
|
1007
|
+
return;
|
|
1008
|
+
}
|
|
1009
|
+
let worker;
|
|
1010
|
+
try {
|
|
1011
|
+
worker = createXlsxWorker(baseUrl, createWorker);
|
|
1012
|
+
} catch (error) {
|
|
1013
|
+
if (cancelled || requestId !== requestIdRef.current) return;
|
|
1014
|
+
const normalized = error instanceof Error ? error : new Error(String(error));
|
|
1015
|
+
failDocument?.(normalized);
|
|
1016
|
+
setState({
|
|
1017
|
+
status: "error",
|
|
1018
|
+
message: normalized.message
|
|
1019
|
+
});
|
|
1020
|
+
return;
|
|
1021
|
+
}
|
|
1022
|
+
if (cancelled || requestId !== requestIdRef.current) {
|
|
1023
|
+
worker.terminate();
|
|
1024
|
+
return;
|
|
1025
|
+
}
|
|
1026
|
+
workerRef.current = worker;
|
|
1027
|
+
worker.onmessage = (event) => {
|
|
1028
|
+
if (cancelled || event.data.id !== requestIdRef.current) return;
|
|
1029
|
+
worker.terminate();
|
|
1030
|
+
if (workerRef.current === worker) workerRef.current = null;
|
|
1031
|
+
if (event.data.ok) {
|
|
1032
|
+
for (const warning of event.data.model.warnings) {
|
|
1033
|
+
if (loggedWarningsRef.current.has(warning)) continue;
|
|
1034
|
+
loggedWarningsRef.current.add(warning);
|
|
1035
|
+
logger.warn(warning);
|
|
1036
|
+
}
|
|
1037
|
+
setState({
|
|
1038
|
+
status: "ready",
|
|
1039
|
+
model: event.data.model
|
|
1040
|
+
});
|
|
1041
|
+
} else {
|
|
1042
|
+
failDocument?.(new Error(event.data.message));
|
|
1043
|
+
setState({
|
|
1044
|
+
status: "error",
|
|
1045
|
+
message: event.data.message
|
|
1046
|
+
});
|
|
1047
|
+
}
|
|
1048
|
+
};
|
|
1049
|
+
worker.onerror = (event) => {
|
|
1050
|
+
if (cancelled || requestId !== requestIdRef.current) return;
|
|
1051
|
+
worker.terminate();
|
|
1052
|
+
if (workerRef.current === worker) workerRef.current = null;
|
|
1053
|
+
failDocument?.(event.error instanceof Error ? event.error : new Error(event.message));
|
|
1054
|
+
setState({
|
|
1055
|
+
status: "error",
|
|
1056
|
+
message: event.message
|
|
1057
|
+
});
|
|
1058
|
+
};
|
|
1059
|
+
const request = {
|
|
1060
|
+
id: requestId,
|
|
1061
|
+
fileName,
|
|
1062
|
+
data: bytes
|
|
1063
|
+
};
|
|
1064
|
+
try {
|
|
1065
|
+
worker.postMessage(request, [bytes]);
|
|
1066
|
+
} catch (error) {
|
|
1067
|
+
worker.terminate();
|
|
1068
|
+
if (workerRef.current === worker) workerRef.current = null;
|
|
1069
|
+
if (cancelled || requestId !== requestIdRef.current) return;
|
|
1070
|
+
const normalized = error instanceof Error ? error : new Error(String(error));
|
|
1071
|
+
failDocument?.(normalized);
|
|
1072
|
+
setState({
|
|
1073
|
+
status: "error",
|
|
1074
|
+
message: normalized.message
|
|
1075
|
+
});
|
|
1076
|
+
}
|
|
1077
|
+
})();
|
|
1078
|
+
return () => {
|
|
1079
|
+
cancelled = true;
|
|
1080
|
+
controller.abort();
|
|
1081
|
+
workerRef.current?.terminate();
|
|
1082
|
+
workerRef.current = null;
|
|
1083
|
+
};
|
|
1084
|
+
}, [
|
|
1085
|
+
document,
|
|
1086
|
+
fileName,
|
|
1087
|
+
logger,
|
|
1088
|
+
failDocument,
|
|
1089
|
+
baseUrl,
|
|
1090
|
+
createWorker
|
|
1091
|
+
]);
|
|
1092
|
+
return state;
|
|
1093
|
+
}
|
|
1094
|
+
function createXlsxWorker(baseUrl, createWorker) {
|
|
1095
|
+
if (createWorker) return createWorker("xlsx");
|
|
1096
|
+
if (baseUrl) return new Worker(new URL("xlsx.worker.js", new URL(baseUrl, document.baseURI)), { type: "module" });
|
|
1097
|
+
return new Worker(new URL(
|
|
1098
|
+
/* @vite-ignore */
|
|
1099
|
+
"" + new URL("assets/xlsx.worker.js", import.meta.url).href,
|
|
1100
|
+
"" + import.meta.url
|
|
1101
|
+
), { type: "module" });
|
|
1102
|
+
}
|
|
1103
|
+
//#endregion
|
|
1104
|
+
//#region ../../node_modules/.pnpm/@tanstack+virtual-core@3.14.0/node_modules/@tanstack/virtual-core/dist/esm/utils.js
|
|
1105
|
+
function memo$1(getDeps, fn, opts) {
|
|
1106
|
+
let deps = opts.initialDeps ?? [];
|
|
1107
|
+
let result;
|
|
1108
|
+
let isInitial = true;
|
|
1109
|
+
function memoizedFunction() {
|
|
1110
|
+
var _a, _b, _c;
|
|
1111
|
+
let depTime;
|
|
1112
|
+
if (opts.key && ((_a = opts.debug) == null ? void 0 : _a.call(opts))) depTime = Date.now();
|
|
1113
|
+
const newDeps = getDeps();
|
|
1114
|
+
if (!(newDeps.length !== deps.length || newDeps.some((dep, index) => deps[index] !== dep))) return result;
|
|
1115
|
+
deps = newDeps;
|
|
1116
|
+
let resultTime;
|
|
1117
|
+
if (opts.key && ((_b = opts.debug) == null ? void 0 : _b.call(opts))) resultTime = Date.now();
|
|
1118
|
+
result = fn(...newDeps);
|
|
1119
|
+
if (opts.key && ((_c = opts.debug) == null ? void 0 : _c.call(opts))) {
|
|
1120
|
+
const depEndTime = Math.round((Date.now() - depTime) * 100) / 100;
|
|
1121
|
+
const resultEndTime = Math.round((Date.now() - resultTime) * 100) / 100;
|
|
1122
|
+
const resultFpsPercentage = resultEndTime / 16;
|
|
1123
|
+
const pad = (str, num) => {
|
|
1124
|
+
str = String(str);
|
|
1125
|
+
while (str.length < num) str = " " + str;
|
|
1126
|
+
return str;
|
|
1127
|
+
};
|
|
1128
|
+
console.info(`%c⏱ ${pad(resultEndTime, 5)} /${pad(depEndTime, 5)} ms`, `
|
|
1129
|
+
font-size: .6rem;
|
|
1130
|
+
font-weight: bold;
|
|
1131
|
+
color: hsl(${Math.max(0, Math.min(120 - 120 * resultFpsPercentage, 120))}deg 100% 31%);`, opts == null ? void 0 : opts.key);
|
|
1132
|
+
}
|
|
1133
|
+
if ((opts == null ? void 0 : opts.onChange) && !(isInitial && opts.skipInitialOnChange)) opts.onChange(result);
|
|
1134
|
+
isInitial = false;
|
|
1135
|
+
return result;
|
|
1136
|
+
}
|
|
1137
|
+
memoizedFunction.updateDeps = (newDeps) => {
|
|
1138
|
+
deps = newDeps;
|
|
1139
|
+
};
|
|
1140
|
+
return memoizedFunction;
|
|
1141
|
+
}
|
|
1142
|
+
function notUndefined(value, msg) {
|
|
1143
|
+
if (value === void 0) throw new Error(`Unexpected undefined${msg ? `: ${msg}` : ""}`);
|
|
1144
|
+
else return value;
|
|
1145
|
+
}
|
|
1146
|
+
var approxEqual = (a, b) => Math.abs(a - b) < 1.01;
|
|
1147
|
+
var debounce = (targetWindow, fn, ms) => {
|
|
1148
|
+
let timeoutId;
|
|
1149
|
+
return function(...args) {
|
|
1150
|
+
targetWindow.clearTimeout(timeoutId);
|
|
1151
|
+
timeoutId = targetWindow.setTimeout(() => fn.apply(this, args), ms);
|
|
1152
|
+
};
|
|
1153
|
+
};
|
|
1154
|
+
//#endregion
|
|
1155
|
+
//#region ../../node_modules/.pnpm/@tanstack+virtual-core@3.14.0/node_modules/@tanstack/virtual-core/dist/esm/index.js
|
|
1156
|
+
var getRect = (element) => {
|
|
1157
|
+
const { offsetWidth, offsetHeight } = element;
|
|
1158
|
+
return {
|
|
1159
|
+
width: offsetWidth,
|
|
1160
|
+
height: offsetHeight
|
|
1161
|
+
};
|
|
1162
|
+
};
|
|
1163
|
+
var defaultKeyExtractor = (index) => index;
|
|
1164
|
+
var defaultRangeExtractor = (range) => {
|
|
1165
|
+
const start = Math.max(range.startIndex - range.overscan, 0);
|
|
1166
|
+
const end = Math.min(range.endIndex + range.overscan, range.count - 1);
|
|
1167
|
+
const arr = [];
|
|
1168
|
+
for (let i = start; i <= end; i++) arr.push(i);
|
|
1169
|
+
return arr;
|
|
1170
|
+
};
|
|
1171
|
+
var observeElementRect = (instance, cb) => {
|
|
1172
|
+
const element = instance.scrollElement;
|
|
1173
|
+
if (!element) return;
|
|
1174
|
+
const targetWindow = instance.targetWindow;
|
|
1175
|
+
if (!targetWindow) return;
|
|
1176
|
+
const handler = (rect) => {
|
|
1177
|
+
const { width, height } = rect;
|
|
1178
|
+
cb({
|
|
1179
|
+
width: Math.round(width),
|
|
1180
|
+
height: Math.round(height)
|
|
1181
|
+
});
|
|
1182
|
+
};
|
|
1183
|
+
handler(getRect(element));
|
|
1184
|
+
if (!targetWindow.ResizeObserver) return () => {};
|
|
1185
|
+
const observer = new targetWindow.ResizeObserver((entries) => {
|
|
1186
|
+
const run = () => {
|
|
1187
|
+
const entry = entries[0];
|
|
1188
|
+
if (entry == null ? void 0 : entry.borderBoxSize) {
|
|
1189
|
+
const box = entry.borderBoxSize[0];
|
|
1190
|
+
if (box) {
|
|
1191
|
+
handler({
|
|
1192
|
+
width: box.inlineSize,
|
|
1193
|
+
height: box.blockSize
|
|
1194
|
+
});
|
|
1195
|
+
return;
|
|
1196
|
+
}
|
|
1197
|
+
}
|
|
1198
|
+
handler(getRect(element));
|
|
1199
|
+
};
|
|
1200
|
+
instance.options.useAnimationFrameWithResizeObserver ? requestAnimationFrame(run) : run();
|
|
1201
|
+
});
|
|
1202
|
+
observer.observe(element, { box: "border-box" });
|
|
1203
|
+
return () => {
|
|
1204
|
+
observer.unobserve(element);
|
|
1205
|
+
};
|
|
1206
|
+
};
|
|
1207
|
+
var addEventListenerOptions = { passive: true };
|
|
1208
|
+
var supportsScrollend = typeof window == "undefined" ? true : "onscrollend" in window;
|
|
1209
|
+
var observeElementOffset = (instance, cb) => {
|
|
1210
|
+
const element = instance.scrollElement;
|
|
1211
|
+
if (!element) return;
|
|
1212
|
+
const targetWindow = instance.targetWindow;
|
|
1213
|
+
if (!targetWindow) return;
|
|
1214
|
+
let offset = 0;
|
|
1215
|
+
const fallback = instance.options.useScrollendEvent && supportsScrollend ? () => void 0 : debounce(targetWindow, () => {
|
|
1216
|
+
cb(offset, false);
|
|
1217
|
+
}, instance.options.isScrollingResetDelay);
|
|
1218
|
+
const createHandler = (isScrolling) => () => {
|
|
1219
|
+
const { horizontal, isRtl } = instance.options;
|
|
1220
|
+
offset = horizontal ? element["scrollLeft"] * (isRtl && -1 || 1) : element["scrollTop"];
|
|
1221
|
+
fallback();
|
|
1222
|
+
cb(offset, isScrolling);
|
|
1223
|
+
};
|
|
1224
|
+
const handler = createHandler(true);
|
|
1225
|
+
const endHandler = createHandler(false);
|
|
1226
|
+
element.addEventListener("scroll", handler, addEventListenerOptions);
|
|
1227
|
+
const registerScrollendEvent = instance.options.useScrollendEvent && supportsScrollend;
|
|
1228
|
+
if (registerScrollendEvent) element.addEventListener("scrollend", endHandler, addEventListenerOptions);
|
|
1229
|
+
return () => {
|
|
1230
|
+
element.removeEventListener("scroll", handler);
|
|
1231
|
+
if (registerScrollendEvent) element.removeEventListener("scrollend", endHandler);
|
|
1232
|
+
};
|
|
1233
|
+
};
|
|
1234
|
+
var measureElement = (element, entry, instance) => {
|
|
1235
|
+
if (entry == null ? void 0 : entry.borderBoxSize) {
|
|
1236
|
+
const box = entry.borderBoxSize[0];
|
|
1237
|
+
if (box) return Math.round(box[instance.options.horizontal ? "inlineSize" : "blockSize"]);
|
|
1238
|
+
}
|
|
1239
|
+
return element[instance.options.horizontal ? "offsetWidth" : "offsetHeight"];
|
|
1240
|
+
};
|
|
1241
|
+
var elementScroll = (offset, { adjustments = 0, behavior }, instance) => {
|
|
1242
|
+
var _a, _b;
|
|
1243
|
+
const toOffset = offset + adjustments;
|
|
1244
|
+
(_b = (_a = instance.scrollElement) == null ? void 0 : _a.scrollTo) == null || _b.call(_a, {
|
|
1245
|
+
[instance.options.horizontal ? "left" : "top"]: toOffset,
|
|
1246
|
+
behavior
|
|
1247
|
+
});
|
|
1248
|
+
};
|
|
1249
|
+
var Virtualizer = class {
|
|
1250
|
+
constructor(opts) {
|
|
1251
|
+
this.unsubs = [];
|
|
1252
|
+
this.scrollElement = null;
|
|
1253
|
+
this.targetWindow = null;
|
|
1254
|
+
this.isScrolling = false;
|
|
1255
|
+
this.scrollState = null;
|
|
1256
|
+
this.measurementsCache = [];
|
|
1257
|
+
this.itemSizeCache = /* @__PURE__ */ new Map();
|
|
1258
|
+
this.laneAssignments = /* @__PURE__ */ new Map();
|
|
1259
|
+
this.pendingMeasuredCacheIndexes = [];
|
|
1260
|
+
this.prevLanes = void 0;
|
|
1261
|
+
this.lanesChangedFlag = false;
|
|
1262
|
+
this.lanesSettling = false;
|
|
1263
|
+
this.scrollRect = null;
|
|
1264
|
+
this.scrollOffset = null;
|
|
1265
|
+
this.scrollDirection = null;
|
|
1266
|
+
this.scrollAdjustments = 0;
|
|
1267
|
+
this.elementsCache = /* @__PURE__ */ new Map();
|
|
1268
|
+
this.now = () => {
|
|
1269
|
+
var _a, _b, _c;
|
|
1270
|
+
return ((_c = (_b = (_a = this.targetWindow) == null ? void 0 : _a.performance) == null ? void 0 : _b.now) == null ? void 0 : _c.call(_b)) ?? Date.now();
|
|
1271
|
+
};
|
|
1272
|
+
this.observer = /* @__PURE__ */ (() => {
|
|
1273
|
+
let _ro = null;
|
|
1274
|
+
const get = () => {
|
|
1275
|
+
if (_ro) return _ro;
|
|
1276
|
+
if (!this.targetWindow || !this.targetWindow.ResizeObserver) return null;
|
|
1277
|
+
return _ro = new this.targetWindow.ResizeObserver((entries) => {
|
|
1278
|
+
entries.forEach((entry) => {
|
|
1279
|
+
const run = () => {
|
|
1280
|
+
const node = entry.target;
|
|
1281
|
+
const index = this.indexFromElement(node);
|
|
1282
|
+
if (!node.isConnected) {
|
|
1283
|
+
this.observer.unobserve(node);
|
|
1284
|
+
return;
|
|
1285
|
+
}
|
|
1286
|
+
if (this.shouldMeasureDuringScroll(index)) this.resizeItem(index, this.options.measureElement(node, entry, this));
|
|
1287
|
+
};
|
|
1288
|
+
this.options.useAnimationFrameWithResizeObserver ? requestAnimationFrame(run) : run();
|
|
1289
|
+
});
|
|
1290
|
+
});
|
|
1291
|
+
};
|
|
1292
|
+
return {
|
|
1293
|
+
disconnect: () => {
|
|
1294
|
+
var _a;
|
|
1295
|
+
(_a = get()) == null || _a.disconnect();
|
|
1296
|
+
_ro = null;
|
|
1297
|
+
},
|
|
1298
|
+
observe: (target) => {
|
|
1299
|
+
var _a;
|
|
1300
|
+
return (_a = get()) == null ? void 0 : _a.observe(target, { box: "border-box" });
|
|
1301
|
+
},
|
|
1302
|
+
unobserve: (target) => {
|
|
1303
|
+
var _a;
|
|
1304
|
+
return (_a = get()) == null ? void 0 : _a.unobserve(target);
|
|
1305
|
+
}
|
|
1306
|
+
};
|
|
1307
|
+
})();
|
|
1308
|
+
this.range = null;
|
|
1309
|
+
this.setOptions = (opts2) => {
|
|
1310
|
+
Object.entries(opts2).forEach(([key, value]) => {
|
|
1311
|
+
if (typeof value === "undefined") delete opts2[key];
|
|
1312
|
+
});
|
|
1313
|
+
this.options = {
|
|
1314
|
+
debug: false,
|
|
1315
|
+
initialOffset: 0,
|
|
1316
|
+
overscan: 1,
|
|
1317
|
+
paddingStart: 0,
|
|
1318
|
+
paddingEnd: 0,
|
|
1319
|
+
scrollPaddingStart: 0,
|
|
1320
|
+
scrollPaddingEnd: 0,
|
|
1321
|
+
horizontal: false,
|
|
1322
|
+
getItemKey: defaultKeyExtractor,
|
|
1323
|
+
rangeExtractor: defaultRangeExtractor,
|
|
1324
|
+
onChange: () => {},
|
|
1325
|
+
measureElement,
|
|
1326
|
+
initialRect: {
|
|
1327
|
+
width: 0,
|
|
1328
|
+
height: 0
|
|
1329
|
+
},
|
|
1330
|
+
scrollMargin: 0,
|
|
1331
|
+
gap: 0,
|
|
1332
|
+
indexAttribute: "data-index",
|
|
1333
|
+
initialMeasurementsCache: [],
|
|
1334
|
+
lanes: 1,
|
|
1335
|
+
isScrollingResetDelay: 150,
|
|
1336
|
+
enabled: true,
|
|
1337
|
+
isRtl: false,
|
|
1338
|
+
useScrollendEvent: false,
|
|
1339
|
+
useAnimationFrameWithResizeObserver: false,
|
|
1340
|
+
laneAssignmentMode: "estimate",
|
|
1341
|
+
...opts2
|
|
1342
|
+
};
|
|
1343
|
+
};
|
|
1344
|
+
this.notify = (sync) => {
|
|
1345
|
+
var _a, _b;
|
|
1346
|
+
(_b = (_a = this.options).onChange) == null || _b.call(_a, this, sync);
|
|
1347
|
+
};
|
|
1348
|
+
this.maybeNotify = memo$1(() => {
|
|
1349
|
+
this.calculateRange();
|
|
1350
|
+
return [
|
|
1351
|
+
this.isScrolling,
|
|
1352
|
+
this.range ? this.range.startIndex : null,
|
|
1353
|
+
this.range ? this.range.endIndex : null
|
|
1354
|
+
];
|
|
1355
|
+
}, (isScrolling) => {
|
|
1356
|
+
this.notify(isScrolling);
|
|
1357
|
+
}, {
|
|
1358
|
+
key: false,
|
|
1359
|
+
debug: () => this.options.debug,
|
|
1360
|
+
initialDeps: [
|
|
1361
|
+
this.isScrolling,
|
|
1362
|
+
this.range ? this.range.startIndex : null,
|
|
1363
|
+
this.range ? this.range.endIndex : null
|
|
1364
|
+
]
|
|
1365
|
+
});
|
|
1366
|
+
this.cleanup = () => {
|
|
1367
|
+
this.unsubs.filter(Boolean).forEach((d) => d());
|
|
1368
|
+
this.unsubs = [];
|
|
1369
|
+
this.observer.disconnect();
|
|
1370
|
+
if (this.rafId != null && this.targetWindow) {
|
|
1371
|
+
this.targetWindow.cancelAnimationFrame(this.rafId);
|
|
1372
|
+
this.rafId = null;
|
|
1373
|
+
}
|
|
1374
|
+
this.scrollState = null;
|
|
1375
|
+
this.scrollElement = null;
|
|
1376
|
+
this.targetWindow = null;
|
|
1377
|
+
};
|
|
1378
|
+
this._didMount = () => {
|
|
1379
|
+
return () => {
|
|
1380
|
+
this.cleanup();
|
|
1381
|
+
};
|
|
1382
|
+
};
|
|
1383
|
+
this._willUpdate = () => {
|
|
1384
|
+
var _a;
|
|
1385
|
+
const scrollElement = this.options.enabled ? this.options.getScrollElement() : null;
|
|
1386
|
+
if (this.scrollElement !== scrollElement) {
|
|
1387
|
+
this.cleanup();
|
|
1388
|
+
if (!scrollElement) {
|
|
1389
|
+
this.maybeNotify();
|
|
1390
|
+
return;
|
|
1391
|
+
}
|
|
1392
|
+
this.scrollElement = scrollElement;
|
|
1393
|
+
if (this.scrollElement && "ownerDocument" in this.scrollElement) this.targetWindow = this.scrollElement.ownerDocument.defaultView;
|
|
1394
|
+
else this.targetWindow = ((_a = this.scrollElement) == null ? void 0 : _a.window) ?? null;
|
|
1395
|
+
this.elementsCache.forEach((cached) => {
|
|
1396
|
+
this.observer.observe(cached);
|
|
1397
|
+
});
|
|
1398
|
+
this.unsubs.push(this.options.observeElementRect(this, (rect) => {
|
|
1399
|
+
this.scrollRect = rect;
|
|
1400
|
+
this.maybeNotify();
|
|
1401
|
+
}));
|
|
1402
|
+
this.unsubs.push(this.options.observeElementOffset(this, (offset, isScrolling) => {
|
|
1403
|
+
this.scrollAdjustments = 0;
|
|
1404
|
+
this.scrollDirection = isScrolling ? this.getScrollOffset() < offset ? "forward" : "backward" : null;
|
|
1405
|
+
this.scrollOffset = offset;
|
|
1406
|
+
this.isScrolling = isScrolling;
|
|
1407
|
+
if (this.scrollState) this.scheduleScrollReconcile();
|
|
1408
|
+
this.maybeNotify();
|
|
1409
|
+
}));
|
|
1410
|
+
this._scrollToOffset(this.getScrollOffset(), {
|
|
1411
|
+
adjustments: void 0,
|
|
1412
|
+
behavior: void 0
|
|
1413
|
+
});
|
|
1414
|
+
}
|
|
1415
|
+
};
|
|
1416
|
+
this.rafId = null;
|
|
1417
|
+
this.getSize = () => {
|
|
1418
|
+
if (!this.options.enabled) {
|
|
1419
|
+
this.scrollRect = null;
|
|
1420
|
+
return 0;
|
|
1421
|
+
}
|
|
1422
|
+
this.scrollRect = this.scrollRect ?? this.options.initialRect;
|
|
1423
|
+
return this.scrollRect[this.options.horizontal ? "width" : "height"];
|
|
1424
|
+
};
|
|
1425
|
+
this.getScrollOffset = () => {
|
|
1426
|
+
if (!this.options.enabled) {
|
|
1427
|
+
this.scrollOffset = null;
|
|
1428
|
+
return 0;
|
|
1429
|
+
}
|
|
1430
|
+
this.scrollOffset = this.scrollOffset ?? (typeof this.options.initialOffset === "function" ? this.options.initialOffset() : this.options.initialOffset);
|
|
1431
|
+
return this.scrollOffset;
|
|
1432
|
+
};
|
|
1433
|
+
this.getFurthestMeasurement = (measurements, index) => {
|
|
1434
|
+
const furthestMeasurementsFound = /* @__PURE__ */ new Map();
|
|
1435
|
+
const furthestMeasurements = /* @__PURE__ */ new Map();
|
|
1436
|
+
for (let m = index - 1; m >= 0; m--) {
|
|
1437
|
+
const measurement = measurements[m];
|
|
1438
|
+
if (furthestMeasurementsFound.has(measurement.lane)) continue;
|
|
1439
|
+
const previousFurthestMeasurement = furthestMeasurements.get(measurement.lane);
|
|
1440
|
+
if (previousFurthestMeasurement == null || measurement.end > previousFurthestMeasurement.end) furthestMeasurements.set(measurement.lane, measurement);
|
|
1441
|
+
else if (measurement.end < previousFurthestMeasurement.end) furthestMeasurementsFound.set(measurement.lane, true);
|
|
1442
|
+
if (furthestMeasurementsFound.size === this.options.lanes) break;
|
|
1443
|
+
}
|
|
1444
|
+
return furthestMeasurements.size === this.options.lanes ? Array.from(furthestMeasurements.values()).sort((a, b) => {
|
|
1445
|
+
if (a.end === b.end) return a.index - b.index;
|
|
1446
|
+
return a.end - b.end;
|
|
1447
|
+
})[0] : void 0;
|
|
1448
|
+
};
|
|
1449
|
+
this.getMeasurementOptions = memo$1(() => [
|
|
1450
|
+
this.options.count,
|
|
1451
|
+
this.options.paddingStart,
|
|
1452
|
+
this.options.scrollMargin,
|
|
1453
|
+
this.options.getItemKey,
|
|
1454
|
+
this.options.enabled,
|
|
1455
|
+
this.options.lanes,
|
|
1456
|
+
this.options.laneAssignmentMode
|
|
1457
|
+
], (count, paddingStart, scrollMargin, getItemKey, enabled, lanes, laneAssignmentMode) => {
|
|
1458
|
+
if (this.prevLanes !== void 0 && this.prevLanes !== lanes) this.lanesChangedFlag = true;
|
|
1459
|
+
this.prevLanes = lanes;
|
|
1460
|
+
this.pendingMeasuredCacheIndexes = [];
|
|
1461
|
+
return {
|
|
1462
|
+
count,
|
|
1463
|
+
paddingStart,
|
|
1464
|
+
scrollMargin,
|
|
1465
|
+
getItemKey,
|
|
1466
|
+
enabled,
|
|
1467
|
+
lanes,
|
|
1468
|
+
laneAssignmentMode
|
|
1469
|
+
};
|
|
1470
|
+
}, { key: false });
|
|
1471
|
+
this.getMeasurements = memo$1(() => [this.getMeasurementOptions(), this.itemSizeCache], ({ count, paddingStart, scrollMargin, getItemKey, enabled, lanes, laneAssignmentMode }, itemSizeCache) => {
|
|
1472
|
+
if (!enabled) {
|
|
1473
|
+
this.measurementsCache = [];
|
|
1474
|
+
this.itemSizeCache.clear();
|
|
1475
|
+
this.laneAssignments.clear();
|
|
1476
|
+
return [];
|
|
1477
|
+
}
|
|
1478
|
+
if (this.laneAssignments.size > count) {
|
|
1479
|
+
for (const index of this.laneAssignments.keys()) if (index >= count) this.laneAssignments.delete(index);
|
|
1480
|
+
}
|
|
1481
|
+
if (this.lanesChangedFlag) {
|
|
1482
|
+
this.lanesChangedFlag = false;
|
|
1483
|
+
this.lanesSettling = true;
|
|
1484
|
+
this.measurementsCache = [];
|
|
1485
|
+
this.itemSizeCache.clear();
|
|
1486
|
+
this.laneAssignments.clear();
|
|
1487
|
+
this.pendingMeasuredCacheIndexes = [];
|
|
1488
|
+
}
|
|
1489
|
+
if (this.measurementsCache.length === 0 && !this.lanesSettling) {
|
|
1490
|
+
this.measurementsCache = this.options.initialMeasurementsCache;
|
|
1491
|
+
this.measurementsCache.forEach((item) => {
|
|
1492
|
+
this.itemSizeCache.set(item.key, item.size);
|
|
1493
|
+
});
|
|
1494
|
+
}
|
|
1495
|
+
const min = this.lanesSettling ? 0 : this.pendingMeasuredCacheIndexes.length > 0 ? Math.min(...this.pendingMeasuredCacheIndexes) : 0;
|
|
1496
|
+
this.pendingMeasuredCacheIndexes = [];
|
|
1497
|
+
if (this.lanesSettling && this.measurementsCache.length === count) this.lanesSettling = false;
|
|
1498
|
+
const measurements = this.measurementsCache.slice(0, min);
|
|
1499
|
+
const laneLastIndex = new Array(lanes).fill(void 0);
|
|
1500
|
+
for (let m = 0; m < min; m++) {
|
|
1501
|
+
const item = measurements[m];
|
|
1502
|
+
if (item) laneLastIndex[item.lane] = m;
|
|
1503
|
+
}
|
|
1504
|
+
for (let i = min; i < count; i++) {
|
|
1505
|
+
const key = getItemKey(i);
|
|
1506
|
+
const cachedLane = this.laneAssignments.get(i);
|
|
1507
|
+
let lane;
|
|
1508
|
+
let start;
|
|
1509
|
+
const shouldCacheLane = laneAssignmentMode === "estimate" || itemSizeCache.has(key);
|
|
1510
|
+
if (cachedLane !== void 0 && this.options.lanes > 1) {
|
|
1511
|
+
lane = cachedLane;
|
|
1512
|
+
const prevIndex = laneLastIndex[lane];
|
|
1513
|
+
const prevInLane = prevIndex !== void 0 ? measurements[prevIndex] : void 0;
|
|
1514
|
+
start = prevInLane ? prevInLane.end + this.options.gap : paddingStart + scrollMargin;
|
|
1515
|
+
} else {
|
|
1516
|
+
const furthestMeasurement = this.options.lanes === 1 ? measurements[i - 1] : this.getFurthestMeasurement(measurements, i);
|
|
1517
|
+
start = furthestMeasurement ? furthestMeasurement.end + this.options.gap : paddingStart + scrollMargin;
|
|
1518
|
+
lane = furthestMeasurement ? furthestMeasurement.lane : i % this.options.lanes;
|
|
1519
|
+
if (this.options.lanes > 1 && shouldCacheLane) this.laneAssignments.set(i, lane);
|
|
1520
|
+
}
|
|
1521
|
+
const measuredSize = itemSizeCache.get(key);
|
|
1522
|
+
const size = typeof measuredSize === "number" ? measuredSize : this.options.estimateSize(i);
|
|
1523
|
+
const end = start + size;
|
|
1524
|
+
measurements[i] = {
|
|
1525
|
+
index: i,
|
|
1526
|
+
start,
|
|
1527
|
+
size,
|
|
1528
|
+
end,
|
|
1529
|
+
key,
|
|
1530
|
+
lane
|
|
1531
|
+
};
|
|
1532
|
+
laneLastIndex[lane] = i;
|
|
1533
|
+
}
|
|
1534
|
+
this.measurementsCache = measurements;
|
|
1535
|
+
return measurements;
|
|
1536
|
+
}, {
|
|
1537
|
+
key: false,
|
|
1538
|
+
debug: () => this.options.debug
|
|
1539
|
+
});
|
|
1540
|
+
this.calculateRange = memo$1(() => [
|
|
1541
|
+
this.getMeasurements(),
|
|
1542
|
+
this.getSize(),
|
|
1543
|
+
this.getScrollOffset(),
|
|
1544
|
+
this.options.lanes
|
|
1545
|
+
], (measurements, outerSize, scrollOffset, lanes) => {
|
|
1546
|
+
return this.range = measurements.length > 0 && outerSize > 0 ? calculateRange({
|
|
1547
|
+
measurements,
|
|
1548
|
+
outerSize,
|
|
1549
|
+
scrollOffset,
|
|
1550
|
+
lanes
|
|
1551
|
+
}) : null;
|
|
1552
|
+
}, {
|
|
1553
|
+
key: false,
|
|
1554
|
+
debug: () => this.options.debug
|
|
1555
|
+
});
|
|
1556
|
+
this.getVirtualIndexes = memo$1(() => {
|
|
1557
|
+
let startIndex = null;
|
|
1558
|
+
let endIndex = null;
|
|
1559
|
+
const range = this.calculateRange();
|
|
1560
|
+
if (range) {
|
|
1561
|
+
startIndex = range.startIndex;
|
|
1562
|
+
endIndex = range.endIndex;
|
|
1563
|
+
}
|
|
1564
|
+
this.maybeNotify.updateDeps([
|
|
1565
|
+
this.isScrolling,
|
|
1566
|
+
startIndex,
|
|
1567
|
+
endIndex
|
|
1568
|
+
]);
|
|
1569
|
+
return [
|
|
1570
|
+
this.options.rangeExtractor,
|
|
1571
|
+
this.options.overscan,
|
|
1572
|
+
this.options.count,
|
|
1573
|
+
startIndex,
|
|
1574
|
+
endIndex
|
|
1575
|
+
];
|
|
1576
|
+
}, (rangeExtractor, overscan, count, startIndex, endIndex) => {
|
|
1577
|
+
return startIndex === null || endIndex === null ? [] : rangeExtractor({
|
|
1578
|
+
startIndex,
|
|
1579
|
+
endIndex,
|
|
1580
|
+
overscan,
|
|
1581
|
+
count
|
|
1582
|
+
});
|
|
1583
|
+
}, {
|
|
1584
|
+
key: false,
|
|
1585
|
+
debug: () => this.options.debug
|
|
1586
|
+
});
|
|
1587
|
+
this.indexFromElement = (node) => {
|
|
1588
|
+
const attributeName = this.options.indexAttribute;
|
|
1589
|
+
const indexStr = node.getAttribute(attributeName);
|
|
1590
|
+
if (!indexStr) {
|
|
1591
|
+
console.warn(`Missing attribute name '${attributeName}={index}' on measured element.`);
|
|
1592
|
+
return -1;
|
|
1593
|
+
}
|
|
1594
|
+
return parseInt(indexStr, 10);
|
|
1595
|
+
};
|
|
1596
|
+
this.shouldMeasureDuringScroll = (index) => {
|
|
1597
|
+
var _a;
|
|
1598
|
+
if (!this.scrollState || this.scrollState.behavior !== "smooth") return true;
|
|
1599
|
+
const scrollIndex = this.scrollState.index ?? ((_a = this.getVirtualItemForOffset(this.scrollState.lastTargetOffset)) == null ? void 0 : _a.index);
|
|
1600
|
+
if (scrollIndex !== void 0 && this.range) {
|
|
1601
|
+
const bufferSize = Math.max(this.options.overscan, Math.ceil((this.range.endIndex - this.range.startIndex) / 2));
|
|
1602
|
+
const minIndex = Math.max(0, scrollIndex - bufferSize);
|
|
1603
|
+
const maxIndex = Math.min(this.options.count - 1, scrollIndex + bufferSize);
|
|
1604
|
+
return index >= minIndex && index <= maxIndex;
|
|
1605
|
+
}
|
|
1606
|
+
return true;
|
|
1607
|
+
};
|
|
1608
|
+
this.measureElement = (node) => {
|
|
1609
|
+
if (!node) {
|
|
1610
|
+
this.elementsCache.forEach((cached, key2) => {
|
|
1611
|
+
if (!cached.isConnected) {
|
|
1612
|
+
this.observer.unobserve(cached);
|
|
1613
|
+
this.elementsCache.delete(key2);
|
|
1614
|
+
}
|
|
1615
|
+
});
|
|
1616
|
+
return;
|
|
1617
|
+
}
|
|
1618
|
+
const index = this.indexFromElement(node);
|
|
1619
|
+
const key = this.options.getItemKey(index);
|
|
1620
|
+
const prevNode = this.elementsCache.get(key);
|
|
1621
|
+
if (prevNode !== node) {
|
|
1622
|
+
if (prevNode) this.observer.unobserve(prevNode);
|
|
1623
|
+
this.observer.observe(node);
|
|
1624
|
+
this.elementsCache.set(key, node);
|
|
1625
|
+
}
|
|
1626
|
+
if ((!this.isScrolling || this.scrollState) && this.shouldMeasureDuringScroll(index)) this.resizeItem(index, this.options.measureElement(node, void 0, this));
|
|
1627
|
+
};
|
|
1628
|
+
this.resizeItem = (index, size) => {
|
|
1629
|
+
var _a;
|
|
1630
|
+
const item = this.measurementsCache[index];
|
|
1631
|
+
if (!item) return;
|
|
1632
|
+
const delta = size - (this.itemSizeCache.get(item.key) ?? item.size);
|
|
1633
|
+
if (delta !== 0) {
|
|
1634
|
+
if (((_a = this.scrollState) == null ? void 0 : _a.behavior) !== "smooth" && (this.shouldAdjustScrollPositionOnItemSizeChange !== void 0 ? this.shouldAdjustScrollPositionOnItemSizeChange(item, delta, this) : item.start < this.getScrollOffset() + this.scrollAdjustments)) this._scrollToOffset(this.getScrollOffset(), {
|
|
1635
|
+
adjustments: this.scrollAdjustments += delta,
|
|
1636
|
+
behavior: void 0
|
|
1637
|
+
});
|
|
1638
|
+
this.pendingMeasuredCacheIndexes.push(item.index);
|
|
1639
|
+
this.itemSizeCache = new Map(this.itemSizeCache.set(item.key, size));
|
|
1640
|
+
this.notify(false);
|
|
1641
|
+
}
|
|
1642
|
+
};
|
|
1643
|
+
this.getVirtualItems = memo$1(() => [this.getVirtualIndexes(), this.getMeasurements()], (indexes, measurements) => {
|
|
1644
|
+
const virtualItems = [];
|
|
1645
|
+
for (let k = 0, len = indexes.length; k < len; k++) {
|
|
1646
|
+
const measurement = measurements[indexes[k]];
|
|
1647
|
+
virtualItems.push(measurement);
|
|
1648
|
+
}
|
|
1649
|
+
return virtualItems;
|
|
1650
|
+
}, {
|
|
1651
|
+
key: false,
|
|
1652
|
+
debug: () => this.options.debug
|
|
1653
|
+
});
|
|
1654
|
+
this.getVirtualItemForOffset = (offset) => {
|
|
1655
|
+
const measurements = this.getMeasurements();
|
|
1656
|
+
if (measurements.length === 0) return;
|
|
1657
|
+
return notUndefined(measurements[findNearestBinarySearch(0, measurements.length - 1, (index) => notUndefined(measurements[index]).start, offset)]);
|
|
1658
|
+
};
|
|
1659
|
+
this.getMaxScrollOffset = () => {
|
|
1660
|
+
if (!this.scrollElement) return 0;
|
|
1661
|
+
if ("scrollHeight" in this.scrollElement) return this.options.horizontal ? this.scrollElement.scrollWidth - this.scrollElement.clientWidth : this.scrollElement.scrollHeight - this.scrollElement.clientHeight;
|
|
1662
|
+
else {
|
|
1663
|
+
const doc = this.scrollElement.document.documentElement;
|
|
1664
|
+
return this.options.horizontal ? doc.scrollWidth - this.scrollElement.innerWidth : doc.scrollHeight - this.scrollElement.innerHeight;
|
|
1665
|
+
}
|
|
1666
|
+
};
|
|
1667
|
+
this.getOffsetForAlignment = (toOffset, align, itemSize = 0) => {
|
|
1668
|
+
if (!this.scrollElement) return 0;
|
|
1669
|
+
const size = this.getSize();
|
|
1670
|
+
const scrollOffset = this.getScrollOffset();
|
|
1671
|
+
if (align === "auto") align = toOffset >= scrollOffset + size ? "end" : "start";
|
|
1672
|
+
if (align === "center") toOffset += (itemSize - size) / 2;
|
|
1673
|
+
else if (align === "end") toOffset -= size;
|
|
1674
|
+
const maxOffset = this.getMaxScrollOffset();
|
|
1675
|
+
return Math.max(Math.min(maxOffset, toOffset), 0);
|
|
1676
|
+
};
|
|
1677
|
+
this.getOffsetForIndex = (index, align = "auto") => {
|
|
1678
|
+
index = Math.max(0, Math.min(index, this.options.count - 1));
|
|
1679
|
+
const size = this.getSize();
|
|
1680
|
+
const scrollOffset = this.getScrollOffset();
|
|
1681
|
+
const item = this.measurementsCache[index];
|
|
1682
|
+
if (!item) return;
|
|
1683
|
+
if (align === "auto") {
|
|
1684
|
+
if (item.end >= scrollOffset + size - this.options.scrollPaddingEnd) align = "end";
|
|
1685
|
+
else if (item.start <= scrollOffset + this.options.scrollPaddingStart) align = "start";
|
|
1686
|
+
else return [scrollOffset, align];
|
|
1687
|
+
}
|
|
1688
|
+
if (align === "end" && index === this.options.count - 1) return [this.getMaxScrollOffset(), align];
|
|
1689
|
+
const toOffset = align === "end" ? item.end + this.options.scrollPaddingEnd : item.start - this.options.scrollPaddingStart;
|
|
1690
|
+
return [this.getOffsetForAlignment(toOffset, align, item.size), align];
|
|
1691
|
+
};
|
|
1692
|
+
this.scrollToOffset = (toOffset, { align = "start", behavior = "auto" } = {}) => {
|
|
1693
|
+
const offset = this.getOffsetForAlignment(toOffset, align);
|
|
1694
|
+
const now = this.now();
|
|
1695
|
+
this.scrollState = {
|
|
1696
|
+
index: null,
|
|
1697
|
+
align,
|
|
1698
|
+
behavior,
|
|
1699
|
+
startedAt: now,
|
|
1700
|
+
lastTargetOffset: offset,
|
|
1701
|
+
stableFrames: 0
|
|
1702
|
+
};
|
|
1703
|
+
this._scrollToOffset(offset, {
|
|
1704
|
+
adjustments: void 0,
|
|
1705
|
+
behavior
|
|
1706
|
+
});
|
|
1707
|
+
this.scheduleScrollReconcile();
|
|
1708
|
+
};
|
|
1709
|
+
this.scrollToIndex = (index, { align: initialAlign = "auto", behavior = "auto" } = {}) => {
|
|
1710
|
+
index = Math.max(0, Math.min(index, this.options.count - 1));
|
|
1711
|
+
const offsetInfo = this.getOffsetForIndex(index, initialAlign);
|
|
1712
|
+
if (!offsetInfo) return;
|
|
1713
|
+
const [offset, align] = offsetInfo;
|
|
1714
|
+
const now = this.now();
|
|
1715
|
+
this.scrollState = {
|
|
1716
|
+
index,
|
|
1717
|
+
align,
|
|
1718
|
+
behavior,
|
|
1719
|
+
startedAt: now,
|
|
1720
|
+
lastTargetOffset: offset,
|
|
1721
|
+
stableFrames: 0
|
|
1722
|
+
};
|
|
1723
|
+
this._scrollToOffset(offset, {
|
|
1724
|
+
adjustments: void 0,
|
|
1725
|
+
behavior
|
|
1726
|
+
});
|
|
1727
|
+
this.scheduleScrollReconcile();
|
|
1728
|
+
};
|
|
1729
|
+
this.scrollBy = (delta, { behavior = "auto" } = {}) => {
|
|
1730
|
+
const offset = this.getScrollOffset() + delta;
|
|
1731
|
+
const now = this.now();
|
|
1732
|
+
this.scrollState = {
|
|
1733
|
+
index: null,
|
|
1734
|
+
align: "start",
|
|
1735
|
+
behavior,
|
|
1736
|
+
startedAt: now,
|
|
1737
|
+
lastTargetOffset: offset,
|
|
1738
|
+
stableFrames: 0
|
|
1739
|
+
};
|
|
1740
|
+
this._scrollToOffset(offset, {
|
|
1741
|
+
adjustments: void 0,
|
|
1742
|
+
behavior
|
|
1743
|
+
});
|
|
1744
|
+
this.scheduleScrollReconcile();
|
|
1745
|
+
};
|
|
1746
|
+
this.getTotalSize = () => {
|
|
1747
|
+
var _a;
|
|
1748
|
+
const measurements = this.getMeasurements();
|
|
1749
|
+
let end;
|
|
1750
|
+
if (measurements.length === 0) end = this.options.paddingStart;
|
|
1751
|
+
else if (this.options.lanes === 1) end = ((_a = measurements[measurements.length - 1]) == null ? void 0 : _a.end) ?? 0;
|
|
1752
|
+
else {
|
|
1753
|
+
const endByLane = Array(this.options.lanes).fill(null);
|
|
1754
|
+
let endIndex = measurements.length - 1;
|
|
1755
|
+
while (endIndex >= 0 && endByLane.some((val) => val === null)) {
|
|
1756
|
+
const item = measurements[endIndex];
|
|
1757
|
+
if (endByLane[item.lane] === null) endByLane[item.lane] = item.end;
|
|
1758
|
+
endIndex--;
|
|
1759
|
+
}
|
|
1760
|
+
end = Math.max(...endByLane.filter((val) => val !== null));
|
|
1761
|
+
}
|
|
1762
|
+
return Math.max(end - this.options.scrollMargin + this.options.paddingEnd, 0);
|
|
1763
|
+
};
|
|
1764
|
+
this._scrollToOffset = (offset, { adjustments, behavior }) => {
|
|
1765
|
+
this.options.scrollToFn(offset, {
|
|
1766
|
+
behavior,
|
|
1767
|
+
adjustments
|
|
1768
|
+
}, this);
|
|
1769
|
+
};
|
|
1770
|
+
this.measure = () => {
|
|
1771
|
+
this.itemSizeCache = /* @__PURE__ */ new Map();
|
|
1772
|
+
this.laneAssignments = /* @__PURE__ */ new Map();
|
|
1773
|
+
this.notify(false);
|
|
1774
|
+
};
|
|
1775
|
+
this.setOptions(opts);
|
|
1776
|
+
}
|
|
1777
|
+
scheduleScrollReconcile() {
|
|
1778
|
+
if (!this.targetWindow) {
|
|
1779
|
+
this.scrollState = null;
|
|
1780
|
+
return;
|
|
1781
|
+
}
|
|
1782
|
+
if (this.rafId != null) return;
|
|
1783
|
+
this.rafId = this.targetWindow.requestAnimationFrame(() => {
|
|
1784
|
+
this.rafId = null;
|
|
1785
|
+
this.reconcileScroll();
|
|
1786
|
+
});
|
|
1787
|
+
}
|
|
1788
|
+
reconcileScroll() {
|
|
1789
|
+
if (!this.scrollState) return;
|
|
1790
|
+
if (!this.scrollElement) return;
|
|
1791
|
+
if (this.now() - this.scrollState.startedAt > 5e3) {
|
|
1792
|
+
this.scrollState = null;
|
|
1793
|
+
return;
|
|
1794
|
+
}
|
|
1795
|
+
const offsetInfo = this.scrollState.index != null ? this.getOffsetForIndex(this.scrollState.index, this.scrollState.align) : void 0;
|
|
1796
|
+
const targetOffset = offsetInfo ? offsetInfo[0] : this.scrollState.lastTargetOffset;
|
|
1797
|
+
const STABLE_FRAMES = 1;
|
|
1798
|
+
const targetChanged = targetOffset !== this.scrollState.lastTargetOffset;
|
|
1799
|
+
if (!targetChanged && approxEqual(targetOffset, this.getScrollOffset())) {
|
|
1800
|
+
this.scrollState.stableFrames++;
|
|
1801
|
+
if (this.scrollState.stableFrames >= STABLE_FRAMES) {
|
|
1802
|
+
this.scrollState = null;
|
|
1803
|
+
return;
|
|
1804
|
+
}
|
|
1805
|
+
} else {
|
|
1806
|
+
this.scrollState.stableFrames = 0;
|
|
1807
|
+
if (targetChanged) {
|
|
1808
|
+
this.scrollState.lastTargetOffset = targetOffset;
|
|
1809
|
+
this.scrollState.behavior = "auto";
|
|
1810
|
+
this._scrollToOffset(targetOffset, {
|
|
1811
|
+
adjustments: void 0,
|
|
1812
|
+
behavior: "auto"
|
|
1813
|
+
});
|
|
1814
|
+
}
|
|
1815
|
+
}
|
|
1816
|
+
this.scheduleScrollReconcile();
|
|
1817
|
+
}
|
|
1818
|
+
};
|
|
1819
|
+
var findNearestBinarySearch = (low, high, getCurrentValue, value) => {
|
|
1820
|
+
while (low <= high) {
|
|
1821
|
+
const middle = (low + high) / 2 | 0;
|
|
1822
|
+
const currentValue = getCurrentValue(middle);
|
|
1823
|
+
if (currentValue < value) low = middle + 1;
|
|
1824
|
+
else if (currentValue > value) high = middle - 1;
|
|
1825
|
+
else return middle;
|
|
1826
|
+
}
|
|
1827
|
+
if (low > 0) return low - 1;
|
|
1828
|
+
else return 0;
|
|
1829
|
+
};
|
|
1830
|
+
function calculateRange({ measurements, outerSize, scrollOffset, lanes }) {
|
|
1831
|
+
const lastIndex = measurements.length - 1;
|
|
1832
|
+
const getOffset = (index) => measurements[index].start;
|
|
1833
|
+
if (measurements.length <= lanes) return {
|
|
1834
|
+
startIndex: 0,
|
|
1835
|
+
endIndex: lastIndex
|
|
1836
|
+
};
|
|
1837
|
+
let startIndex = findNearestBinarySearch(0, lastIndex, getOffset, scrollOffset);
|
|
1838
|
+
let endIndex = startIndex;
|
|
1839
|
+
if (lanes === 1) while (endIndex < lastIndex && measurements[endIndex].end < scrollOffset + outerSize) endIndex++;
|
|
1840
|
+
else if (lanes > 1) {
|
|
1841
|
+
const endPerLane = Array(lanes).fill(0);
|
|
1842
|
+
while (endIndex < lastIndex && endPerLane.some((pos) => pos < scrollOffset + outerSize)) {
|
|
1843
|
+
const item = measurements[endIndex];
|
|
1844
|
+
endPerLane[item.lane] = item.end;
|
|
1845
|
+
endIndex++;
|
|
1846
|
+
}
|
|
1847
|
+
const startPerLane = Array(lanes).fill(scrollOffset + outerSize);
|
|
1848
|
+
while (startIndex >= 0 && startPerLane.some((pos) => pos >= scrollOffset)) {
|
|
1849
|
+
const item = measurements[startIndex];
|
|
1850
|
+
startPerLane[item.lane] = item.start;
|
|
1851
|
+
startIndex--;
|
|
1852
|
+
}
|
|
1853
|
+
startIndex = Math.max(0, startIndex - startIndex % lanes);
|
|
1854
|
+
endIndex = Math.min(lastIndex, endIndex + (lanes - 1 - endIndex % lanes));
|
|
1855
|
+
}
|
|
1856
|
+
return {
|
|
1857
|
+
startIndex,
|
|
1858
|
+
endIndex
|
|
1859
|
+
};
|
|
1860
|
+
}
|
|
1861
|
+
//#endregion
|
|
1862
|
+
//#region ../../node_modules/.pnpm/@tanstack+react-virtual@3.13.24_react-dom@19.2.3_react@19.2.3__react@19.2.3/node_modules/@tanstack/react-virtual/dist/esm/index.js
|
|
1863
|
+
var useIsomorphicLayoutEffect = typeof document !== "undefined" ? React$1.useLayoutEffect : React$1.useEffect;
|
|
1864
|
+
function useVirtualizerBase({ useFlushSync = true, ...options }) {
|
|
1865
|
+
const rerender = React$1.useReducer(() => ({}), {})[1];
|
|
1866
|
+
const resolvedOptions = {
|
|
1867
|
+
...options,
|
|
1868
|
+
onChange: (instance2, sync) => {
|
|
1869
|
+
var _a;
|
|
1870
|
+
if (useFlushSync && sync) flushSync(rerender);
|
|
1871
|
+
else rerender();
|
|
1872
|
+
(_a = options.onChange) == null || _a.call(options, instance2, sync);
|
|
1873
|
+
}
|
|
1874
|
+
};
|
|
1875
|
+
const [instance] = React$1.useState(() => new Virtualizer(resolvedOptions));
|
|
1876
|
+
instance.setOptions(resolvedOptions);
|
|
1877
|
+
useIsomorphicLayoutEffect(() => {
|
|
1878
|
+
return instance._didMount();
|
|
1879
|
+
}, []);
|
|
1880
|
+
useIsomorphicLayoutEffect(() => {
|
|
1881
|
+
return instance._willUpdate();
|
|
1882
|
+
});
|
|
1883
|
+
return instance;
|
|
1884
|
+
}
|
|
1885
|
+
function useVirtualizer(options) {
|
|
1886
|
+
return useVirtualizerBase({
|
|
1887
|
+
observeElementRect,
|
|
1888
|
+
observeElementOffset,
|
|
1889
|
+
scrollToFn: elementScroll,
|
|
1890
|
+
...options
|
|
1891
|
+
});
|
|
1892
|
+
}
|
|
1893
|
+
//#endregion
|
|
1894
|
+
//#region src/plugins/spreadsheet/gridLayout.ts
|
|
1895
|
+
/** pt -> px. */
|
|
1896
|
+
var ptToPx = (pt) => pt * 96 / 72;
|
|
1897
|
+
/** 1-based column number -> 'A' / 'Z' / 'AA'. */
|
|
1898
|
+
var colName = (col) => {
|
|
1899
|
+
let name = "";
|
|
1900
|
+
let n = col;
|
|
1901
|
+
while (n > 0) {
|
|
1902
|
+
const rem = (n - 1) % 26;
|
|
1903
|
+
name = String.fromCharCode(65 + rem) + name;
|
|
1904
|
+
n = Math.floor((n - 1) / 26);
|
|
1905
|
+
}
|
|
1906
|
+
return name;
|
|
1907
|
+
};
|
|
1908
|
+
/** 1-based (row, col) -> 'B4' style address. */
|
|
1909
|
+
var cellAddress = (row, col) => `${colName(col)}${row}`;
|
|
1910
|
+
/**
|
|
1911
|
+
* Builds one axis layout for row heights or column widths, scaled by zoom.
|
|
1912
|
+
* @param count Item count (rowCount / colCount).
|
|
1913
|
+
* @param defaultSizePx Default size at zoom=1.
|
|
1914
|
+
* @param overrides Sparse override table with 1-based keys. Hidden rows/columns map to 0.
|
|
1915
|
+
* @param zoom Scale factor.
|
|
1916
|
+
*/
|
|
1917
|
+
var buildAxisLayout = (count, defaultSizePx, overrides, zoom) => {
|
|
1918
|
+
const sizes = new Array(count);
|
|
1919
|
+
const offsets = new Array(count);
|
|
1920
|
+
let offset = 0;
|
|
1921
|
+
for (let i = 0; i < count; i++) {
|
|
1922
|
+
const size = (overrides[i + 1] ?? defaultSizePx) * zoom;
|
|
1923
|
+
sizes[i] = size;
|
|
1924
|
+
offsets[i] = offset;
|
|
1925
|
+
offset += size;
|
|
1926
|
+
}
|
|
1927
|
+
return {
|
|
1928
|
+
sizes,
|
|
1929
|
+
offsets,
|
|
1930
|
+
totalSize: offset
|
|
1931
|
+
};
|
|
1932
|
+
};
|
|
1933
|
+
/** Lookup the start offset in px for item i (0-based). Out-of-range indexes return totalSize. */
|
|
1934
|
+
var axisOffset = (layout, index) => {
|
|
1935
|
+
if (index < 0) return 0;
|
|
1936
|
+
if (index >= layout.offsets.length) return layout.totalSize;
|
|
1937
|
+
return layout.offsets[index];
|
|
1938
|
+
};
|
|
1939
|
+
/**
|
|
1940
|
+
* Inverse of axisOffset: the 0-based item containing the given px position, clamped to the layout.
|
|
1941
|
+
* Binary search over the prefix sums, returning the last item whose start offset is <= px, so a run of
|
|
1942
|
+
* zero-size (hidden) items resolves to the visible item after them.
|
|
1943
|
+
*/
|
|
1944
|
+
var axisIndexAt = (layout, px) => {
|
|
1945
|
+
const last = layout.offsets.length - 1;
|
|
1946
|
+
if (last < 0) return 0;
|
|
1947
|
+
const position = Math.max(px, 0);
|
|
1948
|
+
if (position >= layout.totalSize) return last;
|
|
1949
|
+
let low = 0;
|
|
1950
|
+
let high = last;
|
|
1951
|
+
while (low < high) {
|
|
1952
|
+
const mid = low + high + 1 >> 1;
|
|
1953
|
+
if (layout.offsets[mid] <= position) low = mid;
|
|
1954
|
+
else high = mid - 1;
|
|
1955
|
+
}
|
|
1956
|
+
return low;
|
|
1957
|
+
};
|
|
1958
|
+
/** Merged range (1-based inclusive) -> pixel rect, based on row/column AxisLayout. */
|
|
1959
|
+
var mergeRectPx = (merge, rowLayout, colLayout) => {
|
|
1960
|
+
const x = axisOffset(colLayout, merge.left - 1);
|
|
1961
|
+
const y = axisOffset(rowLayout, merge.top - 1);
|
|
1962
|
+
const right = axisOffset(colLayout, merge.right);
|
|
1963
|
+
const bottom = axisOffset(rowLayout, merge.bottom);
|
|
1964
|
+
return {
|
|
1965
|
+
x,
|
|
1966
|
+
y,
|
|
1967
|
+
width: right - x,
|
|
1968
|
+
height: bottom - y
|
|
1969
|
+
};
|
|
1970
|
+
};
|
|
1971
|
+
/**
|
|
1972
|
+
* Merged ranges intersecting the viewport, including master coordinates and pixel rects.
|
|
1973
|
+
* A range is returned even when its master cell has scrolled out, as long as the merged rect intersects the viewport.
|
|
1974
|
+
* This is why the merge layer must render independently instead of relying on cell virtualization.
|
|
1975
|
+
*/
|
|
1976
|
+
var mergesInView = (merges, viewport, rowLayout, colLayout) => {
|
|
1977
|
+
const result = [];
|
|
1978
|
+
for (const merge of merges) {
|
|
1979
|
+
const rect = mergeRectPx(merge, rowLayout, colLayout);
|
|
1980
|
+
if (!(rect.x < viewport.right && rect.x + rect.width > viewport.left && rect.y < viewport.bottom && rect.y + rect.height > viewport.top)) continue;
|
|
1981
|
+
result.push({
|
|
1982
|
+
merge,
|
|
1983
|
+
masterRow: merge.top,
|
|
1984
|
+
masterCol: merge.left,
|
|
1985
|
+
rect
|
|
1986
|
+
});
|
|
1987
|
+
}
|
|
1988
|
+
return result;
|
|
1989
|
+
};
|
|
1990
|
+
/**
|
|
1991
|
+
* Grows a selection rect until it fully contains every merged range it touches, matching Excel: a selection may
|
|
1992
|
+
* never cut a merged cell in half. Iterated to a fixpoint because growing can reach further merges; each round must
|
|
1993
|
+
* grow the rect, so the merge cap (MAX_MERGED_RANGES) bounds both the rounds and the work per round.
|
|
1994
|
+
* @param rect Normalized selection rect (top <= bottom, left <= right).
|
|
1995
|
+
*/
|
|
1996
|
+
var expandRangeToMerges = (rect, merges) => {
|
|
1997
|
+
let { top, left, bottom, right } = rect;
|
|
1998
|
+
let grown = true;
|
|
1999
|
+
while (grown) {
|
|
2000
|
+
grown = false;
|
|
2001
|
+
for (const merge of merges) {
|
|
2002
|
+
if (merge.top > bottom || merge.bottom < top || merge.left > right || merge.right < left) continue;
|
|
2003
|
+
if (merge.top < top) {
|
|
2004
|
+
top = merge.top;
|
|
2005
|
+
grown = true;
|
|
2006
|
+
}
|
|
2007
|
+
if (merge.left < left) {
|
|
2008
|
+
left = merge.left;
|
|
2009
|
+
grown = true;
|
|
2010
|
+
}
|
|
2011
|
+
if (merge.bottom > bottom) {
|
|
2012
|
+
bottom = merge.bottom;
|
|
2013
|
+
grown = true;
|
|
2014
|
+
}
|
|
2015
|
+
if (merge.right > right) {
|
|
2016
|
+
right = merge.right;
|
|
2017
|
+
grown = true;
|
|
2018
|
+
}
|
|
2019
|
+
}
|
|
2020
|
+
}
|
|
2021
|
+
return {
|
|
2022
|
+
top,
|
|
2023
|
+
left,
|
|
2024
|
+
bottom,
|
|
2025
|
+
right
|
|
2026
|
+
};
|
|
2027
|
+
};
|
|
2028
|
+
/** Cell range -> A1 notation ('B2:D8'), normalized to top-left:bottom-right. A single cell renders as 'B2'. */
|
|
2029
|
+
var rangeAddress = (rect) => {
|
|
2030
|
+
const start = cellAddress(Math.min(rect.top, rect.bottom), Math.min(rect.left, rect.right));
|
|
2031
|
+
const end = cellAddress(Math.max(rect.top, rect.bottom), Math.max(rect.left, rect.right));
|
|
2032
|
+
return start === end ? start : `${start}:${end}`;
|
|
2033
|
+
};
|
|
2034
|
+
/** Excel default font size 11pt -> px at zoom=1. Matches the CellStyle.fontSizePx default. */
|
|
2035
|
+
var DEFAULT_FONT_SIZE_PX = ptToPx(11);
|
|
2036
|
+
/** Line-height multiplier for wrapped cell text. */
|
|
2037
|
+
var WRAP_LINE_HEIGHT = 1.3;
|
|
2038
|
+
/**
|
|
2039
|
+
* Number of full text lines a wrapped cell can fit in the given cell height, minimum 1.
|
|
2040
|
+
* Used with -webkit-line-clamp so a short row shows only complete lines instead of slicing the last line in half.
|
|
2041
|
+
*/
|
|
2042
|
+
var wrapClampLines = (cellHeightPx, fontSizePx) => Math.max(1, Math.floor(cellHeightPx / (fontSizePx * WRAP_LINE_HEIGHT)));
|
|
2043
|
+
//#endregion
|
|
2044
|
+
//#region src/plugins/spreadsheet/XlsxGrid.tsx
|
|
2045
|
+
var cornersToRect = (anchor, active) => ({
|
|
2046
|
+
top: Math.min(anchor.row, active.row),
|
|
2047
|
+
left: Math.min(anchor.col, active.col),
|
|
2048
|
+
bottom: Math.max(anchor.row, active.row),
|
|
2049
|
+
right: Math.max(anchor.col, active.col)
|
|
2050
|
+
});
|
|
2051
|
+
/** Rect equality by value: hover and selection are compared by their four coordinates, never by object identity. */
|
|
2052
|
+
var sameRect = (a, b) => a === b || a !== null && b !== null && a.top === b.top && a.left === b.left && a.bottom === b.bottom && a.right === b.right;
|
|
2053
|
+
/** A selection is one logical cell when it is 1x1, or when it is exactly the merged range holding its anchor. */
|
|
2054
|
+
var isSingleLogicalCell = (rect, anchorMerge) => {
|
|
2055
|
+
if (rect.top === rect.bottom && rect.left === rect.right) return true;
|
|
2056
|
+
return anchorMerge !== void 0 && rect.top === anchorMerge.top && rect.left === anchorMerge.left && rect.bottom === anchorMerge.bottom && rect.right === anchorMerge.right;
|
|
2057
|
+
};
|
|
2058
|
+
/** Baseline row/column header sizes in px at zoom=1. */
|
|
2059
|
+
var ROW_HEADER_WIDTH_PX = 44;
|
|
2060
|
+
var COL_HEADER_HEIGHT_PX = 22;
|
|
2061
|
+
/** Extra items rendered outside the virtualized viewport. */
|
|
2062
|
+
var OVERSCAN = 6;
|
|
2063
|
+
/** Blank rows/columns beyond the used range. Fill the viewport first, then leave a small scrollable blank area. */
|
|
2064
|
+
var EXTRA_ROWS = 20;
|
|
2065
|
+
var EXTRA_COLS = 5;
|
|
2066
|
+
/** Default grid line when no cell border is set. Follows the DESIGN.md border token. */
|
|
2067
|
+
var DEFAULT_GRID_LINE = "0.5px solid var(--border)";
|
|
2068
|
+
/** Maximum selected-cell overlay content width in px at zoom=1. Wider content wraps at this width. */
|
|
2069
|
+
var SELECTED_OVERLAY_MAX_WIDTH_PX = 480;
|
|
2070
|
+
/** Default cell alignment when CellStyle.hAlign is unset: numbers right, booleans/errors center, others left. */
|
|
2071
|
+
var defaultHAlign = (cell) => {
|
|
2072
|
+
if (typeof cell.raw === "number") return "right";
|
|
2073
|
+
if (typeof cell.raw === "boolean") return "center";
|
|
2074
|
+
return "left";
|
|
2075
|
+
};
|
|
2076
|
+
var BORDER_STYLE_CSS = {
|
|
2077
|
+
thin: "solid",
|
|
2078
|
+
medium: "solid",
|
|
2079
|
+
thick: "solid",
|
|
2080
|
+
dashed: "dashed",
|
|
2081
|
+
dotted: "dotted",
|
|
2082
|
+
hair: "dotted",
|
|
2083
|
+
double: "double"
|
|
2084
|
+
};
|
|
2085
|
+
var BORDER_WIDTH_PX = {
|
|
2086
|
+
thin: 1,
|
|
2087
|
+
hair: 1,
|
|
2088
|
+
dashed: 1,
|
|
2089
|
+
dotted: 1,
|
|
2090
|
+
medium: 1.5,
|
|
2091
|
+
thick: 2,
|
|
2092
|
+
double: 3
|
|
2093
|
+
};
|
|
2094
|
+
var borderEdgeToCss = (edge) => edge ? `${BORDER_WIDTH_PX[edge.style]}px ${BORDER_STYLE_CSS[edge.style]} ${edge.color}` : void 0;
|
|
2095
|
+
var H_ALIGN_TO_JUSTIFY = {
|
|
2096
|
+
left: "flex-start",
|
|
2097
|
+
center: "center",
|
|
2098
|
+
right: "flex-end",
|
|
2099
|
+
justify: "stretch"
|
|
2100
|
+
};
|
|
2101
|
+
var V_ALIGN_TO_ITEMS = {
|
|
2102
|
+
top: "flex-start",
|
|
2103
|
+
middle: "center",
|
|
2104
|
+
bottom: "flex-end"
|
|
2105
|
+
};
|
|
2106
|
+
/** CellStyle -> inline style in the zoom=1 coordinate space. Positioning and sizing are added by callers. */
|
|
2107
|
+
var cellStyleToCss = (style) => {
|
|
2108
|
+
const css = { fontSize: style?.fontSizePx ?? DEFAULT_FONT_SIZE_PX };
|
|
2109
|
+
if (!style) return css;
|
|
2110
|
+
if (style.fontFamily) css.fontFamily = style.fontFamily;
|
|
2111
|
+
if (style.bold) css.fontWeight = "bold";
|
|
2112
|
+
if (style.italic) css.fontStyle = "italic";
|
|
2113
|
+
if (style.underline && style.strike) css.textDecoration = "underline line-through";
|
|
2114
|
+
else if (style.underline) css.textDecoration = "underline";
|
|
2115
|
+
else if (style.strike) css.textDecoration = "line-through";
|
|
2116
|
+
if (style.color) css.color = style.color;
|
|
2117
|
+
if (style.bg) css.backgroundColor = style.bg;
|
|
2118
|
+
if (style.hAlign) css.justifyContent = H_ALIGN_TO_JUSTIFY[style.hAlign];
|
|
2119
|
+
if (style.vAlign) css.alignItems = V_ALIGN_TO_ITEMS[style.vAlign];
|
|
2120
|
+
if (style.wrap) {
|
|
2121
|
+
css.whiteSpace = "normal";
|
|
2122
|
+
css.wordBreak = "break-word";
|
|
2123
|
+
}
|
|
2124
|
+
if (style.indent) css.paddingLeft = style.indent * 8;
|
|
2125
|
+
const borderRight = borderEdgeToCss(style.borderRight);
|
|
2126
|
+
const borderBottom = borderEdgeToCss(style.borderBottom);
|
|
2127
|
+
const borderTop = borderEdgeToCss(style.borderTop);
|
|
2128
|
+
const borderLeft = borderEdgeToCss(style.borderLeft);
|
|
2129
|
+
if (borderRight) css.borderRight = borderRight;
|
|
2130
|
+
if (borderBottom) css.borderBottom = borderBottom;
|
|
2131
|
+
if (borderTop) css.borderTop = borderTop;
|
|
2132
|
+
if (borderLeft) css.borderLeft = borderLeft;
|
|
2133
|
+
return css;
|
|
2134
|
+
};
|
|
2135
|
+
/** Inline style for the cell text span: hyperlink coloring and whole-line clipping for wrapped cells. */
|
|
2136
|
+
var cellTextStyle = (cell, clampLines) => {
|
|
2137
|
+
if (!cell.hyperlink && !clampLines) return void 0;
|
|
2138
|
+
return {
|
|
2139
|
+
...cell.hyperlink ? {
|
|
2140
|
+
color: "var(--primary)",
|
|
2141
|
+
textDecoration: "underline"
|
|
2142
|
+
} : void 0,
|
|
2143
|
+
...clampLines ? {
|
|
2144
|
+
display: "-webkit-box",
|
|
2145
|
+
WebkitBoxOrient: "vertical",
|
|
2146
|
+
WebkitLineClamp: clampLines,
|
|
2147
|
+
overflow: "hidden",
|
|
2148
|
+
lineHeight: WRAP_LINE_HEIGHT
|
|
2149
|
+
} : void 0
|
|
2150
|
+
};
|
|
2151
|
+
};
|
|
2152
|
+
/** Shared renderer for regular and merged cells. Covered non-master cells pass cell=undefined and keep only background. */
|
|
2153
|
+
var CellView = memo(function CellView({ cell, style, covered = false, isFirstRow, isFirstCol, top, left, width, height }) {
|
|
2154
|
+
const css = cellStyleToCss(style);
|
|
2155
|
+
const hAlign = style?.hAlign ?? (cell ? defaultHAlign(cell) : "left");
|
|
2156
|
+
const clampLines = style?.wrap ? wrapClampLines(height, css.fontSize) : void 0;
|
|
2157
|
+
const finalCss = {
|
|
2158
|
+
position: "absolute",
|
|
2159
|
+
top,
|
|
2160
|
+
left,
|
|
2161
|
+
width,
|
|
2162
|
+
height,
|
|
2163
|
+
...css,
|
|
2164
|
+
display: "flex",
|
|
2165
|
+
justifyContent: css.justifyContent ?? H_ALIGN_TO_JUSTIFY[hAlign],
|
|
2166
|
+
overflow: "hidden",
|
|
2167
|
+
whiteSpace: css.whiteSpace ?? "nowrap",
|
|
2168
|
+
boxSizing: "border-box",
|
|
2169
|
+
borderRight: covered ? void 0 : css.borderRight ?? DEFAULT_GRID_LINE,
|
|
2170
|
+
borderBottom: covered ? void 0 : css.borderBottom ?? DEFAULT_GRID_LINE,
|
|
2171
|
+
borderTop: covered ? void 0 : isFirstRow ? css.borderTop ?? DEFAULT_GRID_LINE : css.borderTop,
|
|
2172
|
+
borderLeft: covered ? void 0 : isFirstCol ? css.borderLeft ?? DEFAULT_GRID_LINE : css.borderLeft
|
|
2173
|
+
};
|
|
2174
|
+
return /* @__PURE__ */ jsx("div", {
|
|
2175
|
+
className: "absolute px-1 text-foreground text-sm",
|
|
2176
|
+
style: finalCss,
|
|
2177
|
+
children: cell && /* @__PURE__ */ jsx("span", {
|
|
2178
|
+
className: cn(cell.formulaState === "unevaluated" && "text-muted-foreground italic"),
|
|
2179
|
+
style: cellTextStyle(cell, clampLines),
|
|
2180
|
+
children: cell.text
|
|
2181
|
+
})
|
|
2182
|
+
});
|
|
2183
|
+
});
|
|
2184
|
+
/**
|
|
2185
|
+
* Selected-cell overlay. It renders full clipped content over the original cell without changing grid layout.
|
|
2186
|
+
*/
|
|
2187
|
+
var SelectedCellOverlay = ({ cell, style, rect }) => {
|
|
2188
|
+
const css = cellStyleToCss(style);
|
|
2189
|
+
const hAlign = style?.hAlign ?? (cell ? defaultHAlign(cell) : "left");
|
|
2190
|
+
const finalCss = {
|
|
2191
|
+
...css,
|
|
2192
|
+
position: "absolute",
|
|
2193
|
+
top: rect.y,
|
|
2194
|
+
left: rect.x,
|
|
2195
|
+
minWidth: rect.width,
|
|
2196
|
+
minHeight: rect.height,
|
|
2197
|
+
maxWidth: SELECTED_OVERLAY_MAX_WIDTH_PX,
|
|
2198
|
+
width: "max-content",
|
|
2199
|
+
display: "flex",
|
|
2200
|
+
justifyContent: css.justifyContent ?? H_ALIGN_TO_JUSTIFY[hAlign],
|
|
2201
|
+
whiteSpace: "pre-wrap",
|
|
2202
|
+
wordBreak: "break-word",
|
|
2203
|
+
lineHeight: WRAP_LINE_HEIGHT,
|
|
2204
|
+
boxSizing: "border-box",
|
|
2205
|
+
backgroundColor: css.backgroundColor ?? "var(--background)"
|
|
2206
|
+
};
|
|
2207
|
+
return /* @__PURE__ */ jsx("div", {
|
|
2208
|
+
"data-testid": "xlsx-grid-selected-overlay",
|
|
2209
|
+
className: "z-10 px-1 text-foreground text-sm shadow-md outline outline-primary",
|
|
2210
|
+
style: finalCss,
|
|
2211
|
+
children: cell && /* @__PURE__ */ jsx("span", {
|
|
2212
|
+
className: cn(cell.formulaState === "unevaluated" && "text-muted-foreground italic"),
|
|
2213
|
+
style: cellTextStyle(cell, void 0),
|
|
2214
|
+
children: cell.text
|
|
2215
|
+
})
|
|
2216
|
+
});
|
|
2217
|
+
};
|
|
2218
|
+
/** Placeholder for unsupported charts: dashed border, centered label, and raw type name. */
|
|
2219
|
+
var UnsupportedChartPlaceholder = ({ chart }) => {
|
|
2220
|
+
const { t } = useTranslation();
|
|
2221
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
2222
|
+
className: "flex h-full w-full flex-col items-center justify-center gap-1 rounded-md border border-border border-dashed text-center text-muted-foreground text-xs",
|
|
2223
|
+
children: [/* @__PURE__ */ jsx("span", { children: t("xlsx_preview.chart_unsupported") }), chart.rawTypeName && /* @__PURE__ */ jsx("span", { children: chart.rawTypeName })]
|
|
2224
|
+
});
|
|
2225
|
+
};
|
|
2226
|
+
/** Chart mount host. The ref callback owns the disposer returned by renderChart and calls it on unmount/replacement. */
|
|
2227
|
+
var ChartHost = ({ chart, renderChart }) => {
|
|
2228
|
+
const { t } = useTranslation();
|
|
2229
|
+
const disposeRef = useRef(null);
|
|
2230
|
+
const setRef = useCallback((node) => {
|
|
2231
|
+
disposeRef.current?.();
|
|
2232
|
+
disposeRef.current = node ? renderChart(chart, node) : null;
|
|
2233
|
+
}, [chart, renderChart]);
|
|
2234
|
+
return /* @__PURE__ */ jsx("div", {
|
|
2235
|
+
ref: setRef,
|
|
2236
|
+
className: "h-full w-full",
|
|
2237
|
+
role: "img",
|
|
2238
|
+
"aria-label": chart.title || t("xlsx_preview.chart")
|
|
2239
|
+
});
|
|
2240
|
+
};
|
|
2241
|
+
/** True for a pointer target inside the floating layer (chart or image), which owns its own pointer interactions. */
|
|
2242
|
+
var isFloatingObject = (target) => target instanceof Element && target.closest("[data-xlsx-floating]") !== null;
|
|
2243
|
+
var XlsxGrid = ({ sheet, styles, imageUrls, zoom, onSelectCell, pickerActive, renderChart }) => {
|
|
2244
|
+
const { options } = usePreviewHost();
|
|
2245
|
+
const scrollElRef = useRef(null);
|
|
2246
|
+
const [selected, setSelected] = useState(null);
|
|
2247
|
+
/** Cell or merged range under the pointer while picking. Null whenever the picker is off. */
|
|
2248
|
+
const [hoverRect, setHoverRect] = useState(null);
|
|
2249
|
+
const selectionRef = useRef(null);
|
|
2250
|
+
const dragRef = useRef(null);
|
|
2251
|
+
const touchTapRef = useRef(null);
|
|
2252
|
+
/** Last pointer client position while picking, so a scroll can re-resolve the hover with the pointer standing still. */
|
|
2253
|
+
const lastPointerRef = useRef(null);
|
|
2254
|
+
const suppressClickRef = useRef(false);
|
|
2255
|
+
const pendingKeyCommitRef = useRef(false);
|
|
2256
|
+
const [viewport, setViewport] = useState({
|
|
2257
|
+
top: 0,
|
|
2258
|
+
left: 0,
|
|
2259
|
+
bottom: 0,
|
|
2260
|
+
right: 0
|
|
2261
|
+
});
|
|
2262
|
+
const viewportHeight = viewport.bottom - viewport.top;
|
|
2263
|
+
const viewportWidth = viewport.right - viewport.left;
|
|
2264
|
+
const renderedRowCount = Math.max(sheet.rowCount, Math.ceil(viewportHeight / (sheet.defaultRowHeightPx * zoom))) + EXTRA_ROWS;
|
|
2265
|
+
const renderedColCount = Math.max(sheet.colCount, Math.ceil(viewportWidth / (sheet.defaultColWidthPx * zoom))) + EXTRA_COLS;
|
|
2266
|
+
const rowLayout = useMemo(() => buildAxisLayout(renderedRowCount, sheet.defaultRowHeightPx, sheet.rowHeightsPx, 1), [
|
|
2267
|
+
renderedRowCount,
|
|
2268
|
+
sheet.defaultRowHeightPx,
|
|
2269
|
+
sheet.rowHeightsPx
|
|
2270
|
+
]);
|
|
2271
|
+
const colLayout = useMemo(() => buildAxisLayout(renderedColCount, sheet.defaultColWidthPx, sheet.colWidthsPx, 1), [
|
|
2272
|
+
renderedColCount,
|
|
2273
|
+
sheet.defaultColWidthPx,
|
|
2274
|
+
sheet.colWidthsPx
|
|
2275
|
+
]);
|
|
2276
|
+
const scaledHeaderWidth = ROW_HEADER_WIDTH_PX * zoom;
|
|
2277
|
+
const scaledHeaderHeight = COL_HEADER_HEIGHT_PX * zoom;
|
|
2278
|
+
const zoomTransform = {
|
|
2279
|
+
transform: `scale(${zoom})`,
|
|
2280
|
+
transformOrigin: "top left"
|
|
2281
|
+
};
|
|
2282
|
+
const rowVirtualizer = useVirtualizer({
|
|
2283
|
+
count: renderedRowCount,
|
|
2284
|
+
getScrollElement: () => scrollElRef.current,
|
|
2285
|
+
estimateSize: (index) => rowLayout.sizes[index] * zoom,
|
|
2286
|
+
overscan: OVERSCAN
|
|
2287
|
+
});
|
|
2288
|
+
const colVirtualizer = useVirtualizer({
|
|
2289
|
+
horizontal: true,
|
|
2290
|
+
count: renderedColCount,
|
|
2291
|
+
getScrollElement: () => scrollElRef.current,
|
|
2292
|
+
estimateSize: (index) => colLayout.sizes[index] * zoom,
|
|
2293
|
+
overscan: OVERSCAN
|
|
2294
|
+
});
|
|
2295
|
+
const virtualRows = rowVirtualizer.getVirtualItems();
|
|
2296
|
+
const virtualCols = colVirtualizer.getVirtualItems();
|
|
2297
|
+
const readViewport = useCallback((el) => ({
|
|
2298
|
+
top: el.scrollTop,
|
|
2299
|
+
left: el.scrollLeft,
|
|
2300
|
+
bottom: el.scrollTop + el.clientHeight,
|
|
2301
|
+
right: el.scrollLeft + el.clientWidth
|
|
2302
|
+
}), []);
|
|
2303
|
+
const scrollElCallback = useCallback((node) => {
|
|
2304
|
+
scrollElRef.current = node;
|
|
2305
|
+
if (node) setViewport(readViewport(node));
|
|
2306
|
+
}, [readViewport]);
|
|
2307
|
+
useEffect(() => {
|
|
2308
|
+
const el = scrollElRef.current;
|
|
2309
|
+
if (!el) return;
|
|
2310
|
+
const observer = new ResizeObserver(() => setViewport(readViewport(el)));
|
|
2311
|
+
observer.observe(el);
|
|
2312
|
+
return () => observer.disconnect();
|
|
2313
|
+
}, [readViewport]);
|
|
2314
|
+
const contentViewport = useMemo(() => ({
|
|
2315
|
+
top: viewport.top / zoom,
|
|
2316
|
+
left: viewport.left / zoom,
|
|
2317
|
+
bottom: viewport.bottom / zoom,
|
|
2318
|
+
right: viewport.right / zoom
|
|
2319
|
+
}), [viewport, zoom]);
|
|
2320
|
+
const mergesVisible = useMemo(() => mergesInView(sheet.merges, contentViewport, rowLayout, colLayout), [
|
|
2321
|
+
sheet.merges,
|
|
2322
|
+
contentViewport,
|
|
2323
|
+
rowLayout,
|
|
2324
|
+
colLayout
|
|
2325
|
+
]);
|
|
2326
|
+
const mergeKeyStride = renderedColCount + 1;
|
|
2327
|
+
const visibleMergeByCell = useMemo(() => {
|
|
2328
|
+
const index = /* @__PURE__ */ new Map();
|
|
2329
|
+
const firstRow = (virtualRows[0]?.index ?? 0) + 1;
|
|
2330
|
+
const lastRow = (virtualRows[virtualRows.length - 1]?.index ?? -1) + 1;
|
|
2331
|
+
const firstCol = (virtualCols[0]?.index ?? 0) + 1;
|
|
2332
|
+
const lastCol = (virtualCols[virtualCols.length - 1]?.index ?? -1) + 1;
|
|
2333
|
+
if (lastRow < firstRow || lastCol < firstCol) return index;
|
|
2334
|
+
for (const { merge } of mergesVisible) {
|
|
2335
|
+
const top = Math.max(merge.top, firstRow);
|
|
2336
|
+
const bottom = Math.min(merge.bottom, lastRow);
|
|
2337
|
+
const left = Math.max(merge.left, firstCol);
|
|
2338
|
+
const right = Math.min(merge.right, lastCol);
|
|
2339
|
+
for (let row = top; row <= bottom; row++) {
|
|
2340
|
+
const rowKey = row * mergeKeyStride;
|
|
2341
|
+
for (let col = left; col <= right; col++) index.set(rowKey + col, merge);
|
|
2342
|
+
}
|
|
2343
|
+
}
|
|
2344
|
+
return index;
|
|
2345
|
+
}, [
|
|
2346
|
+
mergeKeyStride,
|
|
2347
|
+
mergesVisible,
|
|
2348
|
+
virtualCols,
|
|
2349
|
+
virtualRows
|
|
2350
|
+
]);
|
|
2351
|
+
const getCell = useCallback((row, col) => sheet.cells[`${row}:${col}`], [sheet.cells]);
|
|
2352
|
+
const getStyle = useCallback((cell) => cell?.styleId !== void 0 ? styles[cell.styleId] : void 0, [styles]);
|
|
2353
|
+
/** Returns the merged range containing (row, col), if any. */
|
|
2354
|
+
const findMerge = useCallback((row, col) => {
|
|
2355
|
+
const visible = visibleMergeByCell.get(row * mergeKeyStride + col);
|
|
2356
|
+
if (visible) return visible;
|
|
2357
|
+
return sheet.merges.find((merge) => row >= merge.top && row <= merge.bottom && col >= merge.left && col <= merge.right);
|
|
2358
|
+
}, [
|
|
2359
|
+
mergeKeyStride,
|
|
2360
|
+
sheet.merges,
|
|
2361
|
+
visibleMergeByCell
|
|
2362
|
+
]);
|
|
2363
|
+
const applySelection = useCallback((next) => {
|
|
2364
|
+
selectionRef.current = next;
|
|
2365
|
+
setSelected(next);
|
|
2366
|
+
}, []);
|
|
2367
|
+
const commitSelection = useCallback((selection) => {
|
|
2368
|
+
if (!selection) {
|
|
2369
|
+
onSelectCell?.(null);
|
|
2370
|
+
return;
|
|
2371
|
+
}
|
|
2372
|
+
const rect = expandRangeToMerges(cornersToRect(selection.anchor, selection.active), sheet.merges);
|
|
2373
|
+
const single = isSingleLogicalCell(rect, findMerge(selection.anchor.row, selection.anchor.col));
|
|
2374
|
+
onSelectCell?.({
|
|
2375
|
+
range: single ? cellAddress(rect.top, rect.left) : rangeAddress(rect),
|
|
2376
|
+
rect,
|
|
2377
|
+
cell: single ? getCell(rect.top, rect.left) ?? null : null
|
|
2378
|
+
});
|
|
2379
|
+
}, [
|
|
2380
|
+
findMerge,
|
|
2381
|
+
getCell,
|
|
2382
|
+
onSelectCell,
|
|
2383
|
+
sheet.merges
|
|
2384
|
+
]);
|
|
2385
|
+
const selectCell = useCallback((row, col) => {
|
|
2386
|
+
const merge = findMerge(row, col);
|
|
2387
|
+
const cell = {
|
|
2388
|
+
row: merge?.top ?? row,
|
|
2389
|
+
col: merge?.left ?? col
|
|
2390
|
+
};
|
|
2391
|
+
const next = {
|
|
2392
|
+
anchor: cell,
|
|
2393
|
+
active: cell
|
|
2394
|
+
};
|
|
2395
|
+
applySelection(next);
|
|
2396
|
+
commitSelection(next);
|
|
2397
|
+
}, [
|
|
2398
|
+
applySelection,
|
|
2399
|
+
commitSelection,
|
|
2400
|
+
findMerge
|
|
2401
|
+
]);
|
|
2402
|
+
const clearSelection = useCallback(() => {
|
|
2403
|
+
dragRef.current = null;
|
|
2404
|
+
pendingKeyCommitRef.current = false;
|
|
2405
|
+
applySelection(null);
|
|
2406
|
+
commitSelection(null);
|
|
2407
|
+
}, [applySelection, commitSelection]);
|
|
2408
|
+
const pickerWasActiveRef = useRef(false);
|
|
2409
|
+
useEffect(() => {
|
|
2410
|
+
const isActive = Boolean(pickerActive);
|
|
2411
|
+
if (pickerWasActiveRef.current === isActive) return;
|
|
2412
|
+
pickerWasActiveRef.current = isActive;
|
|
2413
|
+
if (isActive) clearSelection();
|
|
2414
|
+
else {
|
|
2415
|
+
lastPointerRef.current = null;
|
|
2416
|
+
setHoverRect(null);
|
|
2417
|
+
}
|
|
2418
|
+
}, [pickerActive, clearSelection]);
|
|
2419
|
+
const moveSelection = useCallback((dRow, dCol) => {
|
|
2420
|
+
if (!selected) {
|
|
2421
|
+
selectCell(1, 1);
|
|
2422
|
+
rowVirtualizer.scrollToIndex(0);
|
|
2423
|
+
colVirtualizer.scrollToIndex(0);
|
|
2424
|
+
return;
|
|
2425
|
+
}
|
|
2426
|
+
const from = selected.active;
|
|
2427
|
+
const merge = findMerge(from.row, from.col);
|
|
2428
|
+
let nextRow = from.row;
|
|
2429
|
+
let nextCol = from.col;
|
|
2430
|
+
if (dRow > 0) nextRow = (merge?.bottom ?? from.row) + 1;
|
|
2431
|
+
else if (dRow < 0) nextRow = from.row - 1;
|
|
2432
|
+
if (dCol > 0) nextCol = (merge?.right ?? from.col) + 1;
|
|
2433
|
+
else if (dCol < 0) nextCol = from.col - 1;
|
|
2434
|
+
nextRow = Math.min(Math.max(nextRow, 1), sheet.rowCount);
|
|
2435
|
+
nextCol = Math.min(Math.max(nextCol, 1), sheet.colCount);
|
|
2436
|
+
selectCell(nextRow, nextCol);
|
|
2437
|
+
rowVirtualizer.scrollToIndex(nextRow - 1);
|
|
2438
|
+
colVirtualizer.scrollToIndex(nextCol - 1);
|
|
2439
|
+
}, [
|
|
2440
|
+
selected,
|
|
2441
|
+
findMerge,
|
|
2442
|
+
sheet.rowCount,
|
|
2443
|
+
sheet.colCount,
|
|
2444
|
+
selectCell,
|
|
2445
|
+
rowVirtualizer,
|
|
2446
|
+
colVirtualizer
|
|
2447
|
+
]);
|
|
2448
|
+
const extendSelection = useCallback((dRow, dCol) => {
|
|
2449
|
+
const current = selectionRef.current;
|
|
2450
|
+
if (!current) {
|
|
2451
|
+
selectCell(1, 1);
|
|
2452
|
+
rowVirtualizer.scrollToIndex(0);
|
|
2453
|
+
colVirtualizer.scrollToIndex(0);
|
|
2454
|
+
return;
|
|
2455
|
+
}
|
|
2456
|
+
const active = {
|
|
2457
|
+
row: Math.min(Math.max(current.active.row + dRow, 1), sheet.rowCount),
|
|
2458
|
+
col: Math.min(Math.max(current.active.col + dCol, 1), sheet.colCount)
|
|
2459
|
+
};
|
|
2460
|
+
applySelection({
|
|
2461
|
+
anchor: current.anchor,
|
|
2462
|
+
active
|
|
2463
|
+
});
|
|
2464
|
+
pendingKeyCommitRef.current = true;
|
|
2465
|
+
rowVirtualizer.scrollToIndex(active.row - 1);
|
|
2466
|
+
colVirtualizer.scrollToIndex(active.col - 1);
|
|
2467
|
+
}, [
|
|
2468
|
+
applySelection,
|
|
2469
|
+
colVirtualizer,
|
|
2470
|
+
rowVirtualizer,
|
|
2471
|
+
selectCell,
|
|
2472
|
+
sheet.colCount,
|
|
2473
|
+
sheet.rowCount
|
|
2474
|
+
]);
|
|
2475
|
+
const handleKeyDown = useCallback((e) => {
|
|
2476
|
+
const step = e.shiftKey ? extendSelection : moveSelection;
|
|
2477
|
+
switch (e.key) {
|
|
2478
|
+
case "Escape":
|
|
2479
|
+
clearSelection();
|
|
2480
|
+
return;
|
|
2481
|
+
case "ArrowUp":
|
|
2482
|
+
e.preventDefault();
|
|
2483
|
+
step(-1, 0);
|
|
2484
|
+
return;
|
|
2485
|
+
case "ArrowDown":
|
|
2486
|
+
e.preventDefault();
|
|
2487
|
+
step(1, 0);
|
|
2488
|
+
return;
|
|
2489
|
+
case "ArrowLeft":
|
|
2490
|
+
e.preventDefault();
|
|
2491
|
+
step(0, -1);
|
|
2492
|
+
return;
|
|
2493
|
+
case "ArrowRight":
|
|
2494
|
+
e.preventDefault();
|
|
2495
|
+
step(0, 1);
|
|
2496
|
+
return;
|
|
2497
|
+
case "Enter":
|
|
2498
|
+
case " ": {
|
|
2499
|
+
e.preventDefault();
|
|
2500
|
+
const target = selected?.active ?? {
|
|
2501
|
+
row: 1,
|
|
2502
|
+
col: 1
|
|
2503
|
+
};
|
|
2504
|
+
selectCell(target.row, target.col);
|
|
2505
|
+
rowVirtualizer.scrollToIndex(target.row - 1);
|
|
2506
|
+
colVirtualizer.scrollToIndex(target.col - 1);
|
|
2507
|
+
return;
|
|
2508
|
+
}
|
|
2509
|
+
}
|
|
2510
|
+
}, [
|
|
2511
|
+
clearSelection,
|
|
2512
|
+
extendSelection,
|
|
2513
|
+
moveSelection,
|
|
2514
|
+
selected,
|
|
2515
|
+
selectCell,
|
|
2516
|
+
rowVirtualizer,
|
|
2517
|
+
colVirtualizer
|
|
2518
|
+
]);
|
|
2519
|
+
const commitPendingKeySelection = useCallback(() => {
|
|
2520
|
+
if (!pendingKeyCommitRef.current) return;
|
|
2521
|
+
pendingKeyCommitRef.current = false;
|
|
2522
|
+
commitSelection(selectionRef.current);
|
|
2523
|
+
}, [commitSelection]);
|
|
2524
|
+
/** Pointer position -> 1-based cell, plus whether the pointer is over the sticky row/column headers. */
|
|
2525
|
+
const cellAtPointer = useCallback((clientX, clientY) => {
|
|
2526
|
+
const el = scrollElRef.current;
|
|
2527
|
+
if (!el) return null;
|
|
2528
|
+
const bounds = el.getBoundingClientRect();
|
|
2529
|
+
const offsetX = clientX - bounds.left;
|
|
2530
|
+
const offsetY = clientY - bounds.top;
|
|
2531
|
+
const x = (offsetX + el.scrollLeft - scaledHeaderWidth) / zoom;
|
|
2532
|
+
const y = (offsetY + el.scrollTop - scaledHeaderHeight) / zoom;
|
|
2533
|
+
return {
|
|
2534
|
+
row: axisIndexAt(rowLayout, y) + 1,
|
|
2535
|
+
col: axisIndexAt(colLayout, x) + 1,
|
|
2536
|
+
inHeader: offsetX < scaledHeaderWidth || offsetY < scaledHeaderHeight
|
|
2537
|
+
};
|
|
2538
|
+
}, [
|
|
2539
|
+
colLayout,
|
|
2540
|
+
rowLayout,
|
|
2541
|
+
scaledHeaderHeight,
|
|
2542
|
+
scaledHeaderWidth,
|
|
2543
|
+
zoom
|
|
2544
|
+
]);
|
|
2545
|
+
const selectionAtPointer = useCallback((clientX, clientY, extend) => {
|
|
2546
|
+
const target = cellAtPointer(clientX, clientY);
|
|
2547
|
+
if (!target || target.inHeader || target.row > sheet.rowCount || target.col > sheet.colCount) return null;
|
|
2548
|
+
const merge = findMerge(target.row, target.col);
|
|
2549
|
+
const cell = {
|
|
2550
|
+
row: merge?.top ?? target.row,
|
|
2551
|
+
col: merge?.left ?? target.col
|
|
2552
|
+
};
|
|
2553
|
+
const current = selectionRef.current;
|
|
2554
|
+
return extend && current !== null ? {
|
|
2555
|
+
anchor: current.anchor,
|
|
2556
|
+
active: cell
|
|
2557
|
+
} : {
|
|
2558
|
+
anchor: cell,
|
|
2559
|
+
active: cell
|
|
2560
|
+
};
|
|
2561
|
+
}, [
|
|
2562
|
+
cellAtPointer,
|
|
2563
|
+
findMerge,
|
|
2564
|
+
sheet.colCount,
|
|
2565
|
+
sheet.rowCount
|
|
2566
|
+
]);
|
|
2567
|
+
const handlePointerDown = useCallback((e) => {
|
|
2568
|
+
suppressClickRef.current = false;
|
|
2569
|
+
if (isFloatingObject(e.target)) return;
|
|
2570
|
+
if (e.button !== 0) return;
|
|
2571
|
+
if (e.pointerType === "touch") {
|
|
2572
|
+
touchTapRef.current = e.pointerId;
|
|
2573
|
+
return;
|
|
2574
|
+
}
|
|
2575
|
+
if (pickerActive) setHoverRect(null);
|
|
2576
|
+
const next = selectionAtPointer(e.clientX, e.clientY, e.shiftKey);
|
|
2577
|
+
if (!next) return;
|
|
2578
|
+
applySelection(next);
|
|
2579
|
+
dragRef.current = {
|
|
2580
|
+
pointerId: e.pointerId,
|
|
2581
|
+
selection: next
|
|
2582
|
+
};
|
|
2583
|
+
e.currentTarget.setPointerCapture?.(e.pointerId);
|
|
2584
|
+
}, [
|
|
2585
|
+
applySelection,
|
|
2586
|
+
pickerActive,
|
|
2587
|
+
selectionAtPointer
|
|
2588
|
+
]);
|
|
2589
|
+
const updateHoverRect = useCallback((next) => {
|
|
2590
|
+
setHoverRect((current) => sameRect(current, next) ? current : next);
|
|
2591
|
+
}, []);
|
|
2592
|
+
const resolveHover = useCallback((clientX, clientY) => {
|
|
2593
|
+
const hovered = cellAtPointer(clientX, clientY);
|
|
2594
|
+
if (!hovered || hovered.inHeader || hovered.row > sheet.rowCount || hovered.col > sheet.colCount) {
|
|
2595
|
+
updateHoverRect(null);
|
|
2596
|
+
return;
|
|
2597
|
+
}
|
|
2598
|
+
const hoveredMerge = findMerge(hovered.row, hovered.col);
|
|
2599
|
+
updateHoverRect(hoveredMerge ?? {
|
|
2600
|
+
top: hovered.row,
|
|
2601
|
+
left: hovered.col,
|
|
2602
|
+
bottom: hovered.row,
|
|
2603
|
+
right: hovered.col
|
|
2604
|
+
});
|
|
2605
|
+
}, [
|
|
2606
|
+
cellAtPointer,
|
|
2607
|
+
findMerge,
|
|
2608
|
+
sheet.colCount,
|
|
2609
|
+
sheet.rowCount,
|
|
2610
|
+
updateHoverRect
|
|
2611
|
+
]);
|
|
2612
|
+
const handleScroll = useCallback((e) => {
|
|
2613
|
+
setViewport(readViewport(e.currentTarget));
|
|
2614
|
+
const last = lastPointerRef.current;
|
|
2615
|
+
if (!pickerActive || dragRef.current || !last) return;
|
|
2616
|
+
resolveHover(last.x, last.y);
|
|
2617
|
+
}, [
|
|
2618
|
+
pickerActive,
|
|
2619
|
+
readViewport,
|
|
2620
|
+
resolveHover
|
|
2621
|
+
]);
|
|
2622
|
+
const handlePointerMove = useCallback((e) => {
|
|
2623
|
+
const drag = dragRef.current;
|
|
2624
|
+
if (!drag) {
|
|
2625
|
+
if (!pickerActive) return;
|
|
2626
|
+
if (isFloatingObject(e.target)) {
|
|
2627
|
+
lastPointerRef.current = null;
|
|
2628
|
+
updateHoverRect(null);
|
|
2629
|
+
return;
|
|
2630
|
+
}
|
|
2631
|
+
lastPointerRef.current = {
|
|
2632
|
+
x: e.clientX,
|
|
2633
|
+
y: e.clientY
|
|
2634
|
+
};
|
|
2635
|
+
resolveHover(e.clientX, e.clientY);
|
|
2636
|
+
return;
|
|
2637
|
+
}
|
|
2638
|
+
if (drag.pointerId !== e.pointerId) return;
|
|
2639
|
+
const target = cellAtPointer(e.clientX, e.clientY);
|
|
2640
|
+
if (!target) return;
|
|
2641
|
+
const clamped = {
|
|
2642
|
+
row: Math.min(Math.max(target.row, 1), sheet.rowCount),
|
|
2643
|
+
col: Math.min(Math.max(target.col, 1), sheet.colCount)
|
|
2644
|
+
};
|
|
2645
|
+
const merge = findMerge(clamped.row, clamped.col);
|
|
2646
|
+
const active = {
|
|
2647
|
+
row: merge?.top ?? clamped.row,
|
|
2648
|
+
col: merge?.left ?? clamped.col
|
|
2649
|
+
};
|
|
2650
|
+
if (active.row === drag.selection.active.row && active.col === drag.selection.active.col) return;
|
|
2651
|
+
const next = {
|
|
2652
|
+
anchor: drag.selection.anchor,
|
|
2653
|
+
active
|
|
2654
|
+
};
|
|
2655
|
+
dragRef.current = {
|
|
2656
|
+
pointerId: drag.pointerId,
|
|
2657
|
+
selection: next
|
|
2658
|
+
};
|
|
2659
|
+
applySelection(next);
|
|
2660
|
+
}, [
|
|
2661
|
+
applySelection,
|
|
2662
|
+
cellAtPointer,
|
|
2663
|
+
findMerge,
|
|
2664
|
+
pickerActive,
|
|
2665
|
+
resolveHover,
|
|
2666
|
+
sheet.colCount,
|
|
2667
|
+
sheet.rowCount,
|
|
2668
|
+
updateHoverRect
|
|
2669
|
+
]);
|
|
2670
|
+
const handlePointerUp = useCallback((e) => {
|
|
2671
|
+
if (touchTapRef.current === e.pointerId) {
|
|
2672
|
+
touchTapRef.current = null;
|
|
2673
|
+
const tapped = selectionAtPointer(e.clientX, e.clientY, false);
|
|
2674
|
+
if (!tapped) return;
|
|
2675
|
+
applySelection(tapped);
|
|
2676
|
+
suppressClickRef.current = true;
|
|
2677
|
+
commitSelection(tapped);
|
|
2678
|
+
return;
|
|
2679
|
+
}
|
|
2680
|
+
const drag = dragRef.current;
|
|
2681
|
+
if (!drag || drag.pointerId !== e.pointerId) return;
|
|
2682
|
+
dragRef.current = null;
|
|
2683
|
+
e.currentTarget.releasePointerCapture?.(e.pointerId);
|
|
2684
|
+
suppressClickRef.current = true;
|
|
2685
|
+
commitSelection(drag.selection);
|
|
2686
|
+
}, [
|
|
2687
|
+
applySelection,
|
|
2688
|
+
commitSelection,
|
|
2689
|
+
selectionAtPointer
|
|
2690
|
+
]);
|
|
2691
|
+
const handlePointerCancel = useCallback(() => {
|
|
2692
|
+
touchTapRef.current = null;
|
|
2693
|
+
const drag = dragRef.current;
|
|
2694
|
+
if (!drag) return;
|
|
2695
|
+
dragRef.current = null;
|
|
2696
|
+
commitSelection(drag.selection);
|
|
2697
|
+
}, [commitSelection]);
|
|
2698
|
+
const handlePointerLeave = useCallback(() => {
|
|
2699
|
+
lastPointerRef.current = null;
|
|
2700
|
+
if (pickerActive) setHoverRect(null);
|
|
2701
|
+
}, [pickerActive]);
|
|
2702
|
+
const handleClickCapture = useCallback((e) => {
|
|
2703
|
+
if (!suppressClickRef.current) return;
|
|
2704
|
+
suppressClickRef.current = false;
|
|
2705
|
+
e.stopPropagation();
|
|
2706
|
+
}, []);
|
|
2707
|
+
const selectionRect = useMemo(() => selected ? expandRangeToMerges(cornersToRect(selected.anchor, selected.active), sheet.merges) : null, [selected, sheet.merges]);
|
|
2708
|
+
const anchorMerge = selected ? findMerge(selected.anchor.row, selected.anchor.col) : void 0;
|
|
2709
|
+
const isSingleCellSelection = selectionRect !== null && isSingleLogicalCell(selectionRect, anchorMerge);
|
|
2710
|
+
const selectionRectPx = useMemo(() => selectionRect ? mergeRectPx(selectionRect, rowLayout, colLayout) : null, [
|
|
2711
|
+
selectionRect,
|
|
2712
|
+
rowLayout,
|
|
2713
|
+
colLayout
|
|
2714
|
+
]);
|
|
2715
|
+
const hoverRectPx = useMemo(() => hoverRect ? mergeRectPx(hoverRect, rowLayout, colLayout) : null, [
|
|
2716
|
+
hoverRect,
|
|
2717
|
+
rowLayout,
|
|
2718
|
+
colLayout
|
|
2719
|
+
]);
|
|
2720
|
+
const hoverInsidePick = Boolean(hoverRect && selectionRect && hoverRect.top >= selectionRect.top && hoverRect.bottom <= selectionRect.bottom && hoverRect.left >= selectionRect.left && hoverRect.right <= selectionRect.right);
|
|
2721
|
+
const opaqueHeaders = options?.xlsx?.opaqueHeaders === true;
|
|
2722
|
+
const headerBackground = opaqueHeaders ? {
|
|
2723
|
+
backgroundColor: "rgb(from var(--background) r g b / 1)",
|
|
2724
|
+
backgroundImage: "linear-gradient(var(--muted), var(--muted))"
|
|
2725
|
+
} : void 0;
|
|
2726
|
+
const totalWidth = colLayout.totalSize * zoom + scaledHeaderWidth;
|
|
2727
|
+
const totalHeight = rowLayout.totalSize * zoom + scaledHeaderHeight;
|
|
2728
|
+
return /* @__PURE__ */ jsx("div", {
|
|
2729
|
+
ref: scrollElCallback,
|
|
2730
|
+
"data-testid": "xlsx-grid-scroll",
|
|
2731
|
+
"data-picker": pickerActive ? "true" : void 0,
|
|
2732
|
+
className: cn("relative h-full w-full overflow-auto bg-background", pickerActive && "cursor-cell"),
|
|
2733
|
+
onScroll: handleScroll,
|
|
2734
|
+
onKeyDown: handleKeyDown,
|
|
2735
|
+
onKeyUp: commitPendingKeySelection,
|
|
2736
|
+
onBlur: commitPendingKeySelection,
|
|
2737
|
+
onPointerDown: handlePointerDown,
|
|
2738
|
+
onPointerMove: handlePointerMove,
|
|
2739
|
+
onPointerUp: handlePointerUp,
|
|
2740
|
+
onPointerCancel: handlePointerCancel,
|
|
2741
|
+
onPointerLeave: handlePointerLeave,
|
|
2742
|
+
onClickCapture: handleClickCapture,
|
|
2743
|
+
role: "grid",
|
|
2744
|
+
"aria-label": sheet.name,
|
|
2745
|
+
"aria-readonly": true,
|
|
2746
|
+
"aria-multiselectable": true,
|
|
2747
|
+
"aria-rowcount": sheet.rowCount,
|
|
2748
|
+
"aria-colcount": sheet.colCount,
|
|
2749
|
+
tabIndex: 0,
|
|
2750
|
+
children: /* @__PURE__ */ jsxs("div", {
|
|
2751
|
+
className: "relative",
|
|
2752
|
+
style: {
|
|
2753
|
+
width: totalWidth,
|
|
2754
|
+
height: totalHeight
|
|
2755
|
+
},
|
|
2756
|
+
children: [
|
|
2757
|
+
opaqueHeaders ? /* @__PURE__ */ jsx("div", {
|
|
2758
|
+
"aria-hidden": true,
|
|
2759
|
+
className: "sticky top-0 left-0 z-30 border-r border-b border-border",
|
|
2760
|
+
style: {
|
|
2761
|
+
...headerBackground,
|
|
2762
|
+
width: scaledHeaderWidth,
|
|
2763
|
+
height: scaledHeaderHeight,
|
|
2764
|
+
marginBottom: -scaledHeaderHeight
|
|
2765
|
+
}
|
|
2766
|
+
}) : null,
|
|
2767
|
+
/* @__PURE__ */ jsx("div", {
|
|
2768
|
+
className: "sticky top-0 z-20 border-border border-b bg-muted",
|
|
2769
|
+
style: {
|
|
2770
|
+
...headerBackground,
|
|
2771
|
+
height: scaledHeaderHeight,
|
|
2772
|
+
marginLeft: scaledHeaderWidth,
|
|
2773
|
+
width: colLayout.totalSize * zoom
|
|
2774
|
+
},
|
|
2775
|
+
children: /* @__PURE__ */ jsx("div", {
|
|
2776
|
+
className: "absolute",
|
|
2777
|
+
style: zoomTransform,
|
|
2778
|
+
children: virtualCols.map((vc) => /* @__PURE__ */ jsx("div", {
|
|
2779
|
+
className: "absolute flex items-center justify-center border-border border-r text-muted-foreground text-xs",
|
|
2780
|
+
style: {
|
|
2781
|
+
left: axisOffset(colLayout, vc.index),
|
|
2782
|
+
width: colLayout.sizes[vc.index],
|
|
2783
|
+
height: COL_HEADER_HEIGHT_PX
|
|
2784
|
+
},
|
|
2785
|
+
children: colName(vc.index + 1)
|
|
2786
|
+
}, vc.key))
|
|
2787
|
+
})
|
|
2788
|
+
}),
|
|
2789
|
+
/* @__PURE__ */ jsx("div", {
|
|
2790
|
+
className: "sticky left-0 z-20 border-border border-r bg-muted",
|
|
2791
|
+
style: {
|
|
2792
|
+
...headerBackground,
|
|
2793
|
+
width: scaledHeaderWidth,
|
|
2794
|
+
height: rowLayout.totalSize * zoom,
|
|
2795
|
+
top: scaledHeaderHeight
|
|
2796
|
+
},
|
|
2797
|
+
children: /* @__PURE__ */ jsx("div", {
|
|
2798
|
+
className: "absolute",
|
|
2799
|
+
style: zoomTransform,
|
|
2800
|
+
children: virtualRows.map((vr) => /* @__PURE__ */ jsx("div", {
|
|
2801
|
+
className: "absolute flex items-center justify-center border-border border-b text-muted-foreground text-xs",
|
|
2802
|
+
style: {
|
|
2803
|
+
top: axisOffset(rowLayout, vr.index),
|
|
2804
|
+
height: rowLayout.sizes[vr.index],
|
|
2805
|
+
width: ROW_HEADER_WIDTH_PX
|
|
2806
|
+
},
|
|
2807
|
+
children: vr.index + 1
|
|
2808
|
+
}, vr.key))
|
|
2809
|
+
})
|
|
2810
|
+
}),
|
|
2811
|
+
/* @__PURE__ */ jsxs("div", {
|
|
2812
|
+
className: "absolute",
|
|
2813
|
+
"data-testid": "xlsx-grid-zoom-layer",
|
|
2814
|
+
style: {
|
|
2815
|
+
top: scaledHeaderHeight,
|
|
2816
|
+
left: scaledHeaderWidth,
|
|
2817
|
+
...zoomTransform
|
|
2818
|
+
},
|
|
2819
|
+
children: [
|
|
2820
|
+
/* @__PURE__ */ jsx("div", {
|
|
2821
|
+
className: "absolute",
|
|
2822
|
+
children: virtualRows.map((vr) => {
|
|
2823
|
+
const row = vr.index + 1;
|
|
2824
|
+
const isSemanticRow = row <= sheet.rowCount;
|
|
2825
|
+
return /* @__PURE__ */ jsx("div", {
|
|
2826
|
+
role: isSemanticRow ? "row" : void 0,
|
|
2827
|
+
"aria-hidden": isSemanticRow ? void 0 : true,
|
|
2828
|
+
"aria-rowindex": isSemanticRow ? row : void 0,
|
|
2829
|
+
children: virtualCols.map((vc) => {
|
|
2830
|
+
const col = vc.index + 1;
|
|
2831
|
+
const isSemanticCell = isSemanticRow && col <= sheet.colCount;
|
|
2832
|
+
const merge = visibleMergeByCell.get(row * mergeKeyStride + col);
|
|
2833
|
+
const isMaster = merge !== void 0 && merge.top === row && merge.left === col;
|
|
2834
|
+
const isCovered = merge !== void 0 && !isMaster;
|
|
2835
|
+
const exposesGridCell = isSemanticCell && !isCovered;
|
|
2836
|
+
const cell = merge ? void 0 : getCell(row, col);
|
|
2837
|
+
const style = merge ? void 0 : getStyle(cell);
|
|
2838
|
+
const isSelected = !isCovered && selectionRect !== null && row >= selectionRect.top && row <= selectionRect.bottom && col >= selectionRect.left && col <= selectionRect.right;
|
|
2839
|
+
return /* @__PURE__ */ jsx("div", {
|
|
2840
|
+
onClick: isSemanticCell ? () => selectCell(row, col) : void 0,
|
|
2841
|
+
role: exposesGridCell ? "gridcell" : void 0,
|
|
2842
|
+
"aria-hidden": exposesGridCell ? void 0 : true,
|
|
2843
|
+
"aria-colindex": exposesGridCell ? col : void 0,
|
|
2844
|
+
"aria-selected": exposesGridCell ? isSelected : void 0,
|
|
2845
|
+
"aria-colspan": isMaster && exposesGridCell ? merge.right - merge.left + 1 : void 0,
|
|
2846
|
+
"aria-rowspan": isMaster && exposesGridCell ? merge.bottom - merge.top + 1 : void 0,
|
|
2847
|
+
"aria-label": isMaster && exposesGridCell ? getCell(row, col)?.text || void 0 : void 0,
|
|
2848
|
+
children: /* @__PURE__ */ jsx(CellView, {
|
|
2849
|
+
cell,
|
|
2850
|
+
style,
|
|
2851
|
+
covered: isCovered,
|
|
2852
|
+
isFirstRow: row === 1,
|
|
2853
|
+
isFirstCol: col === 1,
|
|
2854
|
+
top: axisOffset(rowLayout, vr.index),
|
|
2855
|
+
left: axisOffset(colLayout, vc.index),
|
|
2856
|
+
width: colLayout.sizes[vc.index],
|
|
2857
|
+
height: rowLayout.sizes[vr.index]
|
|
2858
|
+
})
|
|
2859
|
+
}, vc.key);
|
|
2860
|
+
})
|
|
2861
|
+
}, vr.key);
|
|
2862
|
+
})
|
|
2863
|
+
}),
|
|
2864
|
+
/* @__PURE__ */ jsx("div", {
|
|
2865
|
+
className: "absolute",
|
|
2866
|
+
children: mergesVisible.map(({ merge, masterRow, masterCol, rect }) => {
|
|
2867
|
+
const cell = getCell(masterRow, masterCol);
|
|
2868
|
+
const style = getStyle(cell);
|
|
2869
|
+
return /* @__PURE__ */ jsx("div", {
|
|
2870
|
+
"aria-hidden": true,
|
|
2871
|
+
onClick: () => selectCell(masterRow, masterCol),
|
|
2872
|
+
children: /* @__PURE__ */ jsx("div", {
|
|
2873
|
+
"data-testid": "xlsx-grid-merge-cell",
|
|
2874
|
+
children: /* @__PURE__ */ jsx(CellView, {
|
|
2875
|
+
cell,
|
|
2876
|
+
style,
|
|
2877
|
+
isFirstRow: masterRow === 1,
|
|
2878
|
+
isFirstCol: masterCol === 1,
|
|
2879
|
+
top: rect.y,
|
|
2880
|
+
left: rect.x,
|
|
2881
|
+
width: rect.width,
|
|
2882
|
+
height: rect.height
|
|
2883
|
+
})
|
|
2884
|
+
})
|
|
2885
|
+
}, `merge:${merge.top}:${merge.left}:${merge.bottom}:${merge.right}`);
|
|
2886
|
+
})
|
|
2887
|
+
}),
|
|
2888
|
+
/* @__PURE__ */ jsxs("div", {
|
|
2889
|
+
className: "pointer-events-none absolute",
|
|
2890
|
+
"data-xlsx-floating": "",
|
|
2891
|
+
children: [sheet.floatingImages.map((img, i) => {
|
|
2892
|
+
const src = imageUrls[img.imageId];
|
|
2893
|
+
if (!src) return null;
|
|
2894
|
+
return /* @__PURE__ */ jsx("img", {
|
|
2895
|
+
src,
|
|
2896
|
+
alt: "",
|
|
2897
|
+
"data-testid": "xlsx-grid-floating-image",
|
|
2898
|
+
className: "pointer-events-auto absolute object-contain",
|
|
2899
|
+
style: {
|
|
2900
|
+
top: img.rect.y,
|
|
2901
|
+
left: img.rect.x,
|
|
2902
|
+
width: img.rect.width,
|
|
2903
|
+
height: img.rect.height
|
|
2904
|
+
}
|
|
2905
|
+
}, `img:${img.imageId}:${i}`);
|
|
2906
|
+
}), sheet.charts.map((chart, i) => {
|
|
2907
|
+
const positionStyle = {
|
|
2908
|
+
top: chart.rect.y,
|
|
2909
|
+
left: chart.rect.x,
|
|
2910
|
+
width: chart.rect.width,
|
|
2911
|
+
height: chart.rect.height
|
|
2912
|
+
};
|
|
2913
|
+
return /* @__PURE__ */ jsx("div", {
|
|
2914
|
+
className: "pointer-events-auto absolute",
|
|
2915
|
+
style: positionStyle,
|
|
2916
|
+
"data-testid": "xlsx-grid-chart",
|
|
2917
|
+
children: chart.type === "unsupported" || !renderChart ? /* @__PURE__ */ jsx(UnsupportedChartPlaceholder, { chart }) : /* @__PURE__ */ jsx(ChartHost, {
|
|
2918
|
+
chart,
|
|
2919
|
+
renderChart
|
|
2920
|
+
})
|
|
2921
|
+
}, `chart:${i}`);
|
|
2922
|
+
})]
|
|
2923
|
+
}),
|
|
2924
|
+
pickerActive && hoverRectPx && !hoverInsidePick && /* @__PURE__ */ jsx("div", {
|
|
2925
|
+
"aria-hidden": true,
|
|
2926
|
+
"data-testid": "xlsx-grid-hover-cell",
|
|
2927
|
+
className: "pointer-events-none absolute bg-primary/5 outline-2 outline-primary/40",
|
|
2928
|
+
style: {
|
|
2929
|
+
top: hoverRectPx.y,
|
|
2930
|
+
left: hoverRectPx.x,
|
|
2931
|
+
width: hoverRectPx.width,
|
|
2932
|
+
height: hoverRectPx.height
|
|
2933
|
+
}
|
|
2934
|
+
}),
|
|
2935
|
+
selectionRect && selectionRectPx && (isSingleCellSelection ? /* @__PURE__ */ jsx(SelectedCellOverlay, {
|
|
2936
|
+
cell: getCell(selectionRect.top, selectionRect.left),
|
|
2937
|
+
style: getStyle(getCell(selectionRect.top, selectionRect.left)),
|
|
2938
|
+
rect: selectionRectPx
|
|
2939
|
+
}) : /* @__PURE__ */ jsx("div", {
|
|
2940
|
+
"aria-hidden": true,
|
|
2941
|
+
"data-testid": "xlsx-grid-selection-range",
|
|
2942
|
+
className: "pointer-events-none absolute bg-primary/10 outline-2 outline-primary",
|
|
2943
|
+
style: {
|
|
2944
|
+
top: selectionRectPx.y,
|
|
2945
|
+
left: selectionRectPx.x,
|
|
2946
|
+
width: selectionRectPx.width,
|
|
2947
|
+
height: selectionRectPx.height
|
|
2948
|
+
}
|
|
2949
|
+
}))
|
|
2950
|
+
]
|
|
2951
|
+
})
|
|
2952
|
+
]
|
|
2953
|
+
})
|
|
2954
|
+
});
|
|
2955
|
+
};
|
|
2956
|
+
var XlsxGrid_default = memo(XlsxGrid);
|
|
2957
|
+
//#endregion
|
|
2958
|
+
//#region src/plugins/spreadsheet/SpreadsheetFilePreview.tsx
|
|
2959
|
+
/** Zoom levels. The default index is 2 (=1). */
|
|
2960
|
+
var ZOOM_LEVELS = [
|
|
2961
|
+
.5,
|
|
2962
|
+
.75,
|
|
2963
|
+
1,
|
|
2964
|
+
1.25,
|
|
2965
|
+
1.5,
|
|
2966
|
+
2
|
|
2967
|
+
];
|
|
2968
|
+
var DEFAULT_ZOOM = 1;
|
|
2969
|
+
var formatZoomLabel = (zoom) => `${Math.round(zoom * 100)}%`;
|
|
2970
|
+
/** Visible sheets from model.sheets. If all are hidden, fall back to the first sheet so one tab remains selectable. */
|
|
2971
|
+
var visibleSheets = (model) => {
|
|
2972
|
+
const visible = model.sheets.filter((sheet) => !sheet.hidden);
|
|
2973
|
+
if (visible.length > 0) return visible;
|
|
2974
|
+
return model.sheets.slice(0, 1);
|
|
2975
|
+
};
|
|
2976
|
+
var chartRendererPromise = null;
|
|
2977
|
+
var loadChartRenderer = () => {
|
|
2978
|
+
chartRendererPromise ??= import("./EchartsChartRenderer-e2b5JysX.js").then((module) => module.echartsChartRenderer).catch((error) => {
|
|
2979
|
+
chartRendererPromise = null;
|
|
2980
|
+
throw error;
|
|
2981
|
+
});
|
|
2982
|
+
return chartRendererPromise;
|
|
2983
|
+
};
|
|
2984
|
+
var sheetHasChart = (sheet) => Boolean(sheet && sheet.charts.length > 0);
|
|
2985
|
+
/**
|
|
2986
|
+
* Hard cap on cells visited while building a selection excerpt. sheet.cells is sparse but the rect is not, so a
|
|
2987
|
+
* whole-column selection would otherwise walk millions of empty coordinates on the main thread.
|
|
2988
|
+
*/
|
|
2989
|
+
var EXCERPT_MAX_SCAN_CELLS = 5e4;
|
|
2990
|
+
/**
|
|
2991
|
+
* Plain-text snapshot of a selected range: tab-separated cells, newline-separated rows.
|
|
2992
|
+
* Both budgets are checked while scanning — building the full text first and truncating afterwards is what makes a
|
|
2993
|
+
* full-column selection hang. createPreviewSelection does the exact normalization and truncation.
|
|
2994
|
+
*
|
|
2995
|
+
* Only text that survives normalization counts against the character budget, which is why each cell is charged its
|
|
2996
|
+
* normalized length rather than its raw one. A cell that leaves nothing behind — empty, or holding only whitespace,
|
|
2997
|
+
* the way an export that blanks a cell with a space leaves it — collapses away along with its separators, so
|
|
2998
|
+
* charging for it would let a run of them exhaust the budget and normalize down to an empty excerpt. The scan cap
|
|
2999
|
+
* stays separate — it is what bounds the walk over a sparse selection.
|
|
3000
|
+
*
|
|
3001
|
+
* ExcelJS reports a merged range's value through every cell it covers, so each range is emitted once, at its master.
|
|
3002
|
+
* Merges are swept row by row rather than searched per cell, keeping the walk linear in the scan cap.
|
|
3003
|
+
*/
|
|
3004
|
+
var buildRangeExcerpt = (sheet, rect) => {
|
|
3005
|
+
const intersecting = sheet.merges.filter((m) => m.top <= rect.bottom && m.bottom >= rect.top && m.left <= rect.right && m.right >= rect.left).sort((a, b) => a.top - b.top);
|
|
3006
|
+
const lines = [];
|
|
3007
|
+
let length = 0;
|
|
3008
|
+
let scanned = 0;
|
|
3009
|
+
let nextMerge = 0;
|
|
3010
|
+
let active = [];
|
|
3011
|
+
for (let row = rect.top; row <= rect.bottom; row++) {
|
|
3012
|
+
while (nextMerge < intersecting.length && intersecting[nextMerge].top <= row) active.push(intersecting[nextMerge++]);
|
|
3013
|
+
if (active.length > 0) active = active.filter((merge) => merge.bottom >= row);
|
|
3014
|
+
const values = [];
|
|
3015
|
+
for (let col = rect.left; col <= rect.right; col++) {
|
|
3016
|
+
if (scanned >= EXCERPT_MAX_SCAN_CELLS || length >= 2e3) break;
|
|
3017
|
+
scanned++;
|
|
3018
|
+
const covering = active.find((merge) => merge.left <= col && merge.right >= col);
|
|
3019
|
+
const text = covering !== void 0 && (covering.top !== row || covering.left !== col) ? "" : sheet.cells[`${row}:${col}`]?.text ?? "";
|
|
3020
|
+
values.push(text);
|
|
3021
|
+
const charged = normalizeSelectionText(text).length;
|
|
3022
|
+
if (charged > 0) length += charged + 1;
|
|
3023
|
+
}
|
|
3024
|
+
lines.push(values.join(" "));
|
|
3025
|
+
if (scanned >= EXCERPT_MAX_SCAN_CELLS || length >= 2e3) break;
|
|
3026
|
+
}
|
|
3027
|
+
return lines.join("\n");
|
|
3028
|
+
};
|
|
3029
|
+
/** Read-only XLSX preview with virtualized sheets, formula status, images, and lazily loaded charts. */
|
|
3030
|
+
function SpreadsheetFilePreview({ sourceId, fileName, document: previewDocument, onSelection }) {
|
|
3031
|
+
const logger = usePreviewLogger("SpreadsheetFilePreview");
|
|
3032
|
+
const { options } = usePreviewHost();
|
|
3033
|
+
const { t } = useTranslation();
|
|
3034
|
+
const state = useXlsxWorkbook(previewDocument, fileName);
|
|
3035
|
+
const [zoom, setZoom] = useState(DEFAULT_ZOOM);
|
|
3036
|
+
const [activeSheetName, setActiveSheetName] = useState(null);
|
|
3037
|
+
const [selectedCell, setSelectedCell] = useState(null);
|
|
3038
|
+
const [chartRenderer, setChartRenderer] = useState(null);
|
|
3039
|
+
const [imageUrls, setImageUrls] = useState({});
|
|
3040
|
+
const model = state.status === "ready" ? state.model : null;
|
|
3041
|
+
const sheets = useMemo(() => model ? visibleSheets(model) : [], [model]);
|
|
3042
|
+
const activeSheet = sheets.find((sheet) => sheet.name === activeSheetName) ?? sheets[0] ?? null;
|
|
3043
|
+
useEffect(() => {
|
|
3044
|
+
setSelectedCell(null);
|
|
3045
|
+
if (sheets.length === 0) {
|
|
3046
|
+
setActiveSheetName(null);
|
|
3047
|
+
return;
|
|
3048
|
+
}
|
|
3049
|
+
if (!sheets.some((sheet) => sheet.name === activeSheetName)) setActiveSheetName(sheets[0].name);
|
|
3050
|
+
}, [sheets, activeSheetName]);
|
|
3051
|
+
const handleSelectCell = useCallback((info) => setSelectedCell(info && activeSheet ? {
|
|
3052
|
+
...info,
|
|
3053
|
+
sheet: activeSheet
|
|
3054
|
+
} : null), [activeSheet]);
|
|
3055
|
+
const selectionReference = useMemo(() => {
|
|
3056
|
+
if (!selectedCell || !activeSheet || selectedCell.sheet !== activeSheet) return null;
|
|
3057
|
+
return createPreviewSelection({
|
|
3058
|
+
sourceId,
|
|
3059
|
+
anchor: {
|
|
3060
|
+
format: "xlsx",
|
|
3061
|
+
sheet: activeSheet.name,
|
|
3062
|
+
range: selectedCell.range
|
|
3063
|
+
},
|
|
3064
|
+
excerpt: buildRangeExcerpt(activeSheet, selectedCell.rect),
|
|
3065
|
+
revision: previewDocument.revision
|
|
3066
|
+
});
|
|
3067
|
+
}, [
|
|
3068
|
+
selectedCell,
|
|
3069
|
+
activeSheet,
|
|
3070
|
+
sourceId,
|
|
3071
|
+
previewDocument.revision
|
|
3072
|
+
]);
|
|
3073
|
+
const captureArmedRef = useRef(false);
|
|
3074
|
+
useEffect(() => {
|
|
3075
|
+
if (!onSelection) {
|
|
3076
|
+
captureArmedRef.current = false;
|
|
3077
|
+
return;
|
|
3078
|
+
}
|
|
3079
|
+
if (!captureArmedRef.current) {
|
|
3080
|
+
captureArmedRef.current = true;
|
|
3081
|
+
return;
|
|
3082
|
+
}
|
|
3083
|
+
onSelection(selectionReference);
|
|
3084
|
+
}, [selectionReference, onSelection]);
|
|
3085
|
+
useEffect(() => {
|
|
3086
|
+
if (!model) return;
|
|
3087
|
+
const nextUrls = {};
|
|
3088
|
+
for (const [imageId, image] of Object.entries(model.images)) nextUrls[Number(imageId)] = URL.createObjectURL(new Blob([image.data], { type: image.mime }));
|
|
3089
|
+
setImageUrls(nextUrls);
|
|
3090
|
+
return () => {
|
|
3091
|
+
for (const url of Object.values(nextUrls)) URL.revokeObjectURL(url);
|
|
3092
|
+
};
|
|
3093
|
+
}, [model]);
|
|
3094
|
+
useEffect(() => {
|
|
3095
|
+
if (chartRenderer || !sheets.some((sheet) => sheetHasChart(sheet))) return;
|
|
3096
|
+
let cancelled = false;
|
|
3097
|
+
loadChartRenderer().then((renderer) => {
|
|
3098
|
+
if (!cancelled) setChartRenderer(renderer);
|
|
3099
|
+
}).catch((error) => {
|
|
3100
|
+
if (cancelled) return;
|
|
3101
|
+
const normalized = error instanceof Error ? error : new Error(String(error));
|
|
3102
|
+
logger.error("Failed to load xlsx chart renderer", normalized);
|
|
3103
|
+
});
|
|
3104
|
+
return () => {
|
|
3105
|
+
cancelled = true;
|
|
3106
|
+
};
|
|
3107
|
+
}, [
|
|
3108
|
+
sheets,
|
|
3109
|
+
chartRenderer,
|
|
3110
|
+
logger
|
|
3111
|
+
]);
|
|
3112
|
+
const renderChart = useMemo(() => {
|
|
3113
|
+
if (!chartRenderer) return void 0;
|
|
3114
|
+
return (chart, container) => chartRenderer.render(chart, container);
|
|
3115
|
+
}, [chartRenderer]);
|
|
3116
|
+
const zoomIndex = ZOOM_LEVELS.indexOf(zoom);
|
|
3117
|
+
const zoomOut = useCallback(() => {
|
|
3118
|
+
setZoom((current) => {
|
|
3119
|
+
const index = ZOOM_LEVELS.indexOf(current);
|
|
3120
|
+
return index > 0 ? ZOOM_LEVELS[index - 1] : current;
|
|
3121
|
+
});
|
|
3122
|
+
}, []);
|
|
3123
|
+
const zoomIn = useCallback(() => {
|
|
3124
|
+
setZoom((current) => {
|
|
3125
|
+
const index = ZOOM_LEVELS.indexOf(current);
|
|
3126
|
+
return index >= 0 && index < ZOOM_LEVELS.length - 1 ? ZOOM_LEVELS[index + 1] : current;
|
|
3127
|
+
});
|
|
3128
|
+
}, []);
|
|
3129
|
+
let content;
|
|
3130
|
+
if (state.status === "loading" || state.status === "idle") content = /* @__PURE__ */ jsxs("div", {
|
|
3131
|
+
role: "status",
|
|
3132
|
+
className: "flex h-full w-full items-center justify-center gap-2 bg-background text-muted-foreground text-sm",
|
|
3133
|
+
children: [/* @__PURE__ */ jsx(LoaderCircle, {
|
|
3134
|
+
className: "size-4 animate-spin",
|
|
3135
|
+
"aria-hidden": true
|
|
3136
|
+
}), /* @__PURE__ */ jsx("span", { children: t("file_preview.loading") })]
|
|
3137
|
+
});
|
|
3138
|
+
else if (state.status === "error") content = /* @__PURE__ */ jsx(EmptyState, {
|
|
3139
|
+
icon: CircleAlert,
|
|
3140
|
+
title: t("file_preview.load_error.title"),
|
|
3141
|
+
description: t("xlsx_preview.error.description"),
|
|
3142
|
+
className: "h-full"
|
|
3143
|
+
});
|
|
3144
|
+
else if (state.status === "oversize") content = /* @__PURE__ */ jsx(FilePreviewTooLarge, {
|
|
3145
|
+
sizeBytes: state.sizeBytes,
|
|
3146
|
+
limitBytes: XLSX_PREVIEW_MAX_SIZE_BYTES
|
|
3147
|
+
});
|
|
3148
|
+
else if (!model || !activeSheet) content = null;
|
|
3149
|
+
else {
|
|
3150
|
+
const cellOnActiveSheet = selectedCell && selectedCell.sheet === activeSheet ? selectedCell : null;
|
|
3151
|
+
const selectedCellContent = cellOnActiveSheet?.cell?.formula ? `= ${cellOnActiveSheet.cell.formula}` : cellOnActiveSheet?.cell?.text;
|
|
3152
|
+
const statusBarText = cellOnActiveSheet ? selectedCellContent ? `${cellOnActiveSheet.range} ${selectedCellContent}` : cellOnActiveSheet.range : null;
|
|
3153
|
+
content = /* @__PURE__ */ jsxs("div", {
|
|
3154
|
+
"data-testid": "spreadsheet-file-preview",
|
|
3155
|
+
role: "region",
|
|
3156
|
+
"aria-label": fileName,
|
|
3157
|
+
className: "relative flex h-full w-full flex-col overflow-hidden bg-background",
|
|
3158
|
+
children: [/* @__PURE__ */ jsx("div", {
|
|
3159
|
+
className: "min-h-0 flex-1",
|
|
3160
|
+
children: /* @__PURE__ */ jsx(XlsxGrid_default, {
|
|
3161
|
+
sheet: activeSheet,
|
|
3162
|
+
styles: model.styles,
|
|
3163
|
+
imageUrls,
|
|
3164
|
+
zoom,
|
|
3165
|
+
onSelectCell: handleSelectCell,
|
|
3166
|
+
pickerActive: onSelection !== void 0,
|
|
3167
|
+
renderChart
|
|
3168
|
+
}, activeSheet.name)
|
|
3169
|
+
}), /* @__PURE__ */ jsxs("div", {
|
|
3170
|
+
"data-testid": "xlsx-preview-footer",
|
|
3171
|
+
className: "flex shrink-0 items-center gap-2 border-border-subtle border-t bg-background px-2 py-1",
|
|
3172
|
+
style: options?.bottomInset === "content" ? { paddingBottom: "calc(0.25rem + var(--file-preview-bottom-inset, 0px))" } : void 0,
|
|
3173
|
+
children: [
|
|
3174
|
+
/* @__PURE__ */ jsx(Tabs, {
|
|
3175
|
+
value: activeSheet.name,
|
|
3176
|
+
onValueChange: setActiveSheetName,
|
|
3177
|
+
variant: "line",
|
|
3178
|
+
className: "min-w-0 shrink",
|
|
3179
|
+
children: /* @__PURE__ */ jsx(TabsList, {
|
|
3180
|
+
"aria-label": t("xlsx_preview.sheet_tabs_label"),
|
|
3181
|
+
className: "min-w-0 gap-1 overflow-x-auto",
|
|
3182
|
+
children: sheets.map((sheet) => /* @__PURE__ */ jsx(TabsTrigger, {
|
|
3183
|
+
value: sheet.name,
|
|
3184
|
+
className: "shrink-0 px-2 py-1 text-xs",
|
|
3185
|
+
children: sheet.name
|
|
3186
|
+
}, sheet.name))
|
|
3187
|
+
})
|
|
3188
|
+
}),
|
|
3189
|
+
/* @__PURE__ */ jsxs("div", {
|
|
3190
|
+
className: "flex min-w-0 flex-1 items-center justify-end gap-2 text-muted-foreground text-xs",
|
|
3191
|
+
children: [statusBarText ? /* @__PURE__ */ jsx("span", {
|
|
3192
|
+
className: "selectable cursor-text select-text truncate",
|
|
3193
|
+
"data-testid": "xlsx-preview-status-bar",
|
|
3194
|
+
children: statusBarText
|
|
3195
|
+
}) : null, cellOnActiveSheet?.cell?.formulaState === "unevaluated" ? /* @__PURE__ */ jsx("span", {
|
|
3196
|
+
className: "shrink-0 italic",
|
|
3197
|
+
children: t("xlsx_preview.formula_not_evaluated")
|
|
3198
|
+
}) : null]
|
|
3199
|
+
}),
|
|
3200
|
+
/* @__PURE__ */ jsx(SpreadsheetFilePreviewToolbar, {
|
|
3201
|
+
zoomLabel: formatZoomLabel(zoom),
|
|
3202
|
+
canZoomOut: zoomIndex > 0,
|
|
3203
|
+
canZoomIn: zoomIndex >= 0 && zoomIndex < ZOOM_LEVELS.length - 1,
|
|
3204
|
+
onZoomOut: zoomOut,
|
|
3205
|
+
onZoomIn: zoomIn
|
|
3206
|
+
})
|
|
3207
|
+
]
|
|
3208
|
+
})]
|
|
3209
|
+
});
|
|
3210
|
+
}
|
|
3211
|
+
return /* @__PURE__ */ jsx(FilePreviewLayout.Frame, { children: /* @__PURE__ */ jsx(FilePreviewLayout.Content, {
|
|
3212
|
+
scrollsInternally: true,
|
|
3213
|
+
children: content
|
|
3214
|
+
}) });
|
|
3215
|
+
}
|
|
3216
|
+
//#endregion
|
|
3217
|
+
export { SpreadsheetFilePreview as default };
|