@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.
Files changed (215) hide show
  1. package/LICENSE +661 -0
  2. package/README.md +247 -2
  3. package/dist/Combination-BLg27FvF.js +1044 -0
  4. package/dist/EchartsChartRenderer-e2b5JysX.js +39859 -0
  5. package/dist/FilePreviewTooLarge-DkHa6toZ.js +27 -0
  6. package/dist/ImageFilePreview-BO8rLRZv.js +2171 -0
  7. package/dist/PdfFilePreview-D3Ie0_mm.js +29747 -0
  8. package/dist/PowerPointFilePreview-BLfF0zcm.js +58032 -0
  9. package/dist/SpreadsheetFilePreview-DJdoyeis.js +3217 -0
  10. package/dist/WordFilePreview-DSqt7Uq2.js +4137 -0
  11. package/dist/assets/cmaps/78-EUC-H.bcmap +0 -0
  12. package/dist/assets/cmaps/78-EUC-V.bcmap +0 -0
  13. package/dist/assets/cmaps/78-H.bcmap +0 -0
  14. package/dist/assets/cmaps/78-RKSJ-H.bcmap +0 -0
  15. package/dist/assets/cmaps/78-RKSJ-V.bcmap +0 -0
  16. package/dist/assets/cmaps/78-V.bcmap +0 -0
  17. package/dist/assets/cmaps/78ms-RKSJ-H.bcmap +0 -0
  18. package/dist/assets/cmaps/78ms-RKSJ-V.bcmap +0 -0
  19. package/dist/assets/cmaps/83pv-RKSJ-H.bcmap +0 -0
  20. package/dist/assets/cmaps/90ms-RKSJ-H.bcmap +0 -0
  21. package/dist/assets/cmaps/90ms-RKSJ-V.bcmap +0 -0
  22. package/dist/assets/cmaps/90msp-RKSJ-H.bcmap +0 -0
  23. package/dist/assets/cmaps/90msp-RKSJ-V.bcmap +0 -0
  24. package/dist/assets/cmaps/90pv-RKSJ-H.bcmap +0 -0
  25. package/dist/assets/cmaps/90pv-RKSJ-V.bcmap +0 -0
  26. package/dist/assets/cmaps/Add-H.bcmap +0 -0
  27. package/dist/assets/cmaps/Add-RKSJ-H.bcmap +0 -0
  28. package/dist/assets/cmaps/Add-RKSJ-V.bcmap +0 -0
  29. package/dist/assets/cmaps/Add-V.bcmap +0 -0
  30. package/dist/assets/cmaps/Adobe-CNS1-0.bcmap +0 -0
  31. package/dist/assets/cmaps/Adobe-CNS1-1.bcmap +0 -0
  32. package/dist/assets/cmaps/Adobe-CNS1-2.bcmap +0 -0
  33. package/dist/assets/cmaps/Adobe-CNS1-3.bcmap +0 -0
  34. package/dist/assets/cmaps/Adobe-CNS1-4.bcmap +0 -0
  35. package/dist/assets/cmaps/Adobe-CNS1-5.bcmap +0 -0
  36. package/dist/assets/cmaps/Adobe-CNS1-6.bcmap +0 -0
  37. package/dist/assets/cmaps/Adobe-CNS1-UCS2.bcmap +0 -0
  38. package/dist/assets/cmaps/Adobe-GB1-0.bcmap +0 -0
  39. package/dist/assets/cmaps/Adobe-GB1-1.bcmap +0 -0
  40. package/dist/assets/cmaps/Adobe-GB1-2.bcmap +0 -0
  41. package/dist/assets/cmaps/Adobe-GB1-3.bcmap +0 -0
  42. package/dist/assets/cmaps/Adobe-GB1-4.bcmap +0 -0
  43. package/dist/assets/cmaps/Adobe-GB1-5.bcmap +0 -0
  44. package/dist/assets/cmaps/Adobe-GB1-UCS2.bcmap +0 -0
  45. package/dist/assets/cmaps/Adobe-Japan1-0.bcmap +0 -0
  46. package/dist/assets/cmaps/Adobe-Japan1-1.bcmap +0 -0
  47. package/dist/assets/cmaps/Adobe-Japan1-2.bcmap +0 -0
  48. package/dist/assets/cmaps/Adobe-Japan1-3.bcmap +0 -0
  49. package/dist/assets/cmaps/Adobe-Japan1-4.bcmap +0 -0
  50. package/dist/assets/cmaps/Adobe-Japan1-5.bcmap +0 -0
  51. package/dist/assets/cmaps/Adobe-Japan1-6.bcmap +0 -0
  52. package/dist/assets/cmaps/Adobe-Japan1-UCS2.bcmap +0 -0
  53. package/dist/assets/cmaps/Adobe-Korea1-0.bcmap +0 -0
  54. package/dist/assets/cmaps/Adobe-Korea1-1.bcmap +0 -0
  55. package/dist/assets/cmaps/Adobe-Korea1-2.bcmap +0 -0
  56. package/dist/assets/cmaps/Adobe-Korea1-UCS2.bcmap +0 -0
  57. package/dist/assets/cmaps/B5-H.bcmap +0 -0
  58. package/dist/assets/cmaps/B5-V.bcmap +0 -0
  59. package/dist/assets/cmaps/B5pc-H.bcmap +0 -0
  60. package/dist/assets/cmaps/B5pc-V.bcmap +0 -0
  61. package/dist/assets/cmaps/CNS-EUC-H.bcmap +0 -0
  62. package/dist/assets/cmaps/CNS-EUC-V.bcmap +0 -0
  63. package/dist/assets/cmaps/CNS1-H.bcmap +0 -0
  64. package/dist/assets/cmaps/CNS1-V.bcmap +0 -0
  65. package/dist/assets/cmaps/CNS2-H.bcmap +0 -0
  66. package/dist/assets/cmaps/CNS2-V.bcmap +3 -0
  67. package/dist/assets/cmaps/ETHK-B5-H.bcmap +0 -0
  68. package/dist/assets/cmaps/ETHK-B5-V.bcmap +0 -0
  69. package/dist/assets/cmaps/ETen-B5-H.bcmap +0 -0
  70. package/dist/assets/cmaps/ETen-B5-V.bcmap +0 -0
  71. package/dist/assets/cmaps/ETenms-B5-H.bcmap +3 -0
  72. package/dist/assets/cmaps/ETenms-B5-V.bcmap +0 -0
  73. package/dist/assets/cmaps/EUC-H.bcmap +0 -0
  74. package/dist/assets/cmaps/EUC-V.bcmap +0 -0
  75. package/dist/assets/cmaps/Ext-H.bcmap +0 -0
  76. package/dist/assets/cmaps/Ext-RKSJ-H.bcmap +0 -0
  77. package/dist/assets/cmaps/Ext-RKSJ-V.bcmap +0 -0
  78. package/dist/assets/cmaps/Ext-V.bcmap +0 -0
  79. package/dist/assets/cmaps/GB-EUC-H.bcmap +0 -0
  80. package/dist/assets/cmaps/GB-EUC-V.bcmap +0 -0
  81. package/dist/assets/cmaps/GB-H.bcmap +4 -0
  82. package/dist/assets/cmaps/GB-V.bcmap +0 -0
  83. package/dist/assets/cmaps/GBK-EUC-H.bcmap +0 -0
  84. package/dist/assets/cmaps/GBK-EUC-V.bcmap +0 -0
  85. package/dist/assets/cmaps/GBK2K-H.bcmap +0 -0
  86. package/dist/assets/cmaps/GBK2K-V.bcmap +0 -0
  87. package/dist/assets/cmaps/GBKp-EUC-H.bcmap +0 -0
  88. package/dist/assets/cmaps/GBKp-EUC-V.bcmap +0 -0
  89. package/dist/assets/cmaps/GBT-EUC-H.bcmap +0 -0
  90. package/dist/assets/cmaps/GBT-EUC-V.bcmap +0 -0
  91. package/dist/assets/cmaps/GBT-H.bcmap +0 -0
  92. package/dist/assets/cmaps/GBT-V.bcmap +0 -0
  93. package/dist/assets/cmaps/GBTpc-EUC-H.bcmap +0 -0
  94. package/dist/assets/cmaps/GBTpc-EUC-V.bcmap +0 -0
  95. package/dist/assets/cmaps/GBpc-EUC-H.bcmap +0 -0
  96. package/dist/assets/cmaps/GBpc-EUC-V.bcmap +0 -0
  97. package/dist/assets/cmaps/H.bcmap +0 -0
  98. package/dist/assets/cmaps/HKdla-B5-H.bcmap +0 -0
  99. package/dist/assets/cmaps/HKdla-B5-V.bcmap +0 -0
  100. package/dist/assets/cmaps/HKdlb-B5-H.bcmap +0 -0
  101. package/dist/assets/cmaps/HKdlb-B5-V.bcmap +0 -0
  102. package/dist/assets/cmaps/HKgccs-B5-H.bcmap +0 -0
  103. package/dist/assets/cmaps/HKgccs-B5-V.bcmap +0 -0
  104. package/dist/assets/cmaps/HKm314-B5-H.bcmap +0 -0
  105. package/dist/assets/cmaps/HKm314-B5-V.bcmap +0 -0
  106. package/dist/assets/cmaps/HKm471-B5-H.bcmap +0 -0
  107. package/dist/assets/cmaps/HKm471-B5-V.bcmap +0 -0
  108. package/dist/assets/cmaps/HKscs-B5-H.bcmap +0 -0
  109. package/dist/assets/cmaps/HKscs-B5-V.bcmap +0 -0
  110. package/dist/assets/cmaps/Hankaku.bcmap +0 -0
  111. package/dist/assets/cmaps/Hiragana.bcmap +0 -0
  112. package/dist/assets/cmaps/KSC-EUC-H.bcmap +0 -0
  113. package/dist/assets/cmaps/KSC-EUC-V.bcmap +0 -0
  114. package/dist/assets/cmaps/KSC-H.bcmap +0 -0
  115. package/dist/assets/cmaps/KSC-Johab-H.bcmap +0 -0
  116. package/dist/assets/cmaps/KSC-Johab-V.bcmap +0 -0
  117. package/dist/assets/cmaps/KSC-V.bcmap +0 -0
  118. package/dist/assets/cmaps/KSCms-UHC-H.bcmap +0 -0
  119. package/dist/assets/cmaps/KSCms-UHC-HW-H.bcmap +0 -0
  120. package/dist/assets/cmaps/KSCms-UHC-HW-V.bcmap +0 -0
  121. package/dist/assets/cmaps/KSCms-UHC-V.bcmap +0 -0
  122. package/dist/assets/cmaps/KSCpc-EUC-H.bcmap +0 -0
  123. package/dist/assets/cmaps/KSCpc-EUC-V.bcmap +0 -0
  124. package/dist/assets/cmaps/Katakana.bcmap +0 -0
  125. package/dist/assets/cmaps/LICENSE +36 -0
  126. package/dist/assets/cmaps/NWP-H.bcmap +0 -0
  127. package/dist/assets/cmaps/NWP-V.bcmap +0 -0
  128. package/dist/assets/cmaps/RKSJ-H.bcmap +0 -0
  129. package/dist/assets/cmaps/RKSJ-V.bcmap +0 -0
  130. package/dist/assets/cmaps/Roman.bcmap +0 -0
  131. package/dist/assets/cmaps/UniCNS-UCS2-H.bcmap +0 -0
  132. package/dist/assets/cmaps/UniCNS-UCS2-V.bcmap +0 -0
  133. package/dist/assets/cmaps/UniCNS-UTF16-H.bcmap +0 -0
  134. package/dist/assets/cmaps/UniCNS-UTF16-V.bcmap +0 -0
  135. package/dist/assets/cmaps/UniCNS-UTF32-H.bcmap +0 -0
  136. package/dist/assets/cmaps/UniCNS-UTF32-V.bcmap +0 -0
  137. package/dist/assets/cmaps/UniCNS-UTF8-H.bcmap +0 -0
  138. package/dist/assets/cmaps/UniCNS-UTF8-V.bcmap +0 -0
  139. package/dist/assets/cmaps/UniGB-UCS2-H.bcmap +0 -0
  140. package/dist/assets/cmaps/UniGB-UCS2-V.bcmap +0 -0
  141. package/dist/assets/cmaps/UniGB-UTF16-H.bcmap +0 -0
  142. package/dist/assets/cmaps/UniGB-UTF16-V.bcmap +0 -0
  143. package/dist/assets/cmaps/UniGB-UTF32-H.bcmap +0 -0
  144. package/dist/assets/cmaps/UniGB-UTF32-V.bcmap +0 -0
  145. package/dist/assets/cmaps/UniGB-UTF8-H.bcmap +0 -0
  146. package/dist/assets/cmaps/UniGB-UTF8-V.bcmap +0 -0
  147. package/dist/assets/cmaps/UniJIS-UCS2-H.bcmap +0 -0
  148. package/dist/assets/cmaps/UniJIS-UCS2-HW-H.bcmap +0 -0
  149. package/dist/assets/cmaps/UniJIS-UCS2-HW-V.bcmap +0 -0
  150. package/dist/assets/cmaps/UniJIS-UCS2-V.bcmap +0 -0
  151. package/dist/assets/cmaps/UniJIS-UTF16-H.bcmap +0 -0
  152. package/dist/assets/cmaps/UniJIS-UTF16-V.bcmap +0 -0
  153. package/dist/assets/cmaps/UniJIS-UTF32-H.bcmap +0 -0
  154. package/dist/assets/cmaps/UniJIS-UTF32-V.bcmap +0 -0
  155. package/dist/assets/cmaps/UniJIS-UTF8-H.bcmap +0 -0
  156. package/dist/assets/cmaps/UniJIS-UTF8-V.bcmap +0 -0
  157. package/dist/assets/cmaps/UniJIS2004-UTF16-H.bcmap +0 -0
  158. package/dist/assets/cmaps/UniJIS2004-UTF16-V.bcmap +0 -0
  159. package/dist/assets/cmaps/UniJIS2004-UTF32-H.bcmap +0 -0
  160. package/dist/assets/cmaps/UniJIS2004-UTF32-V.bcmap +0 -0
  161. package/dist/assets/cmaps/UniJIS2004-UTF8-H.bcmap +0 -0
  162. package/dist/assets/cmaps/UniJIS2004-UTF8-V.bcmap +0 -0
  163. package/dist/assets/cmaps/UniJISPro-UCS2-HW-V.bcmap +0 -0
  164. package/dist/assets/cmaps/UniJISPro-UCS2-V.bcmap +0 -0
  165. package/dist/assets/cmaps/UniJISPro-UTF8-V.bcmap +0 -0
  166. package/dist/assets/cmaps/UniJISX0213-UTF32-H.bcmap +0 -0
  167. package/dist/assets/cmaps/UniJISX0213-UTF32-V.bcmap +0 -0
  168. package/dist/assets/cmaps/UniJISX02132004-UTF32-H.bcmap +0 -0
  169. package/dist/assets/cmaps/UniJISX02132004-UTF32-V.bcmap +0 -0
  170. package/dist/assets/cmaps/UniKS-UCS2-H.bcmap +0 -0
  171. package/dist/assets/cmaps/UniKS-UCS2-V.bcmap +0 -0
  172. package/dist/assets/cmaps/UniKS-UTF16-H.bcmap +0 -0
  173. package/dist/assets/cmaps/UniKS-UTF16-V.bcmap +0 -0
  174. package/dist/assets/cmaps/UniKS-UTF32-H.bcmap +0 -0
  175. package/dist/assets/cmaps/UniKS-UTF32-V.bcmap +0 -0
  176. package/dist/assets/cmaps/UniKS-UTF8-H.bcmap +0 -0
  177. package/dist/assets/cmaps/UniKS-UTF8-V.bcmap +0 -0
  178. package/dist/assets/cmaps/V.bcmap +0 -0
  179. package/dist/assets/cmaps/WP-Symbol.bcmap +0 -0
  180. package/dist/assets/pdf.worker.js +77187 -0
  181. package/dist/assets/standard_fonts/FoxitDingbats.pfb +0 -0
  182. package/dist/assets/standard_fonts/FoxitFixed.pfb +0 -0
  183. package/dist/assets/standard_fonts/FoxitFixedBold.pfb +0 -0
  184. package/dist/assets/standard_fonts/FoxitFixedBoldItalic.pfb +0 -0
  185. package/dist/assets/standard_fonts/FoxitFixedItalic.pfb +0 -0
  186. package/dist/assets/standard_fonts/FoxitSerif.pfb +0 -0
  187. package/dist/assets/standard_fonts/FoxitSerifBold.pfb +0 -0
  188. package/dist/assets/standard_fonts/FoxitSerifBoldItalic.pfb +0 -0
  189. package/dist/assets/standard_fonts/FoxitSerifItalic.pfb +0 -0
  190. package/dist/assets/standard_fonts/FoxitSymbol.pfb +0 -0
  191. package/dist/assets/standard_fonts/LICENSE_FOXIT +27 -0
  192. package/dist/assets/standard_fonts/LICENSE_LIBERATION +102 -0
  193. package/dist/assets/standard_fonts/LiberationSans-Bold.ttf +0 -0
  194. package/dist/assets/standard_fonts/LiberationSans-BoldItalic.ttf +0 -0
  195. package/dist/assets/standard_fonts/LiberationSans-Italic.ttf +0 -0
  196. package/dist/assets/standard_fonts/LiberationSans-Regular.ttf +0 -0
  197. package/dist/assets/xlsx.worker.js +61444 -0
  198. package/dist/core.d.ts +2 -0
  199. package/dist/core.js +3 -0
  200. package/dist/documentAnchor-J7GGLU15.js +69 -0
  201. package/dist/imageFilePreviewPlugin-CxIUlEmg.js +29 -0
  202. package/dist/jszip.min-uwqlc4KL.js +3108 -0
  203. package/dist/react.d.ts +105 -0
  204. package/dist/react.js +3114 -0
  205. package/dist/rolldown-runtime-B-lAHAz2.js +37 -0
  206. package/dist/rotate-ccw-YWx7RzY9.js +36 -0
  207. package/dist/selection-CQPD5a6C.js +47 -0
  208. package/dist/source-DjjzeN4t.d.ts +58 -0
  209. package/dist/source-Rx3etU3y.js +28 -0
  210. package/dist/styles.css +8778 -0
  211. package/dist/styles.js +0 -0
  212. package/dist/touchPinch-C4fl-9fg.js +34 -0
  213. package/dist/useTranslation-DMhY277o.js +7056 -0
  214. package/dist/zoom-out-DLOQGt1e.js +66 -0
  215. 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 };