vastgrid 0.1.1 → 0.2.1

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 (312) hide show
  1. package/README.md +175 -13
  2. package/dist/canvas/geometry.d.ts +92 -0
  3. package/dist/canvas/geometry.d.ts.map +1 -0
  4. package/dist/canvas/geometry.js +197 -0
  5. package/dist/canvas/geometry.js.map +1 -0
  6. package/dist/canvas/grid-canvas.d.ts +161 -0
  7. package/dist/canvas/grid-canvas.d.ts.map +1 -0
  8. package/dist/canvas/grid-canvas.js +1146 -0
  9. package/dist/canvas/grid-canvas.js.map +1 -0
  10. package/dist/canvas/hooks.d.ts +61 -0
  11. package/dist/canvas/hooks.d.ts.map +1 -0
  12. package/dist/canvas/hooks.js +2 -0
  13. package/dist/canvas/hooks.js.map +1 -0
  14. package/dist/canvas/keys.d.ts +26 -0
  15. package/dist/canvas/keys.d.ts.map +1 -0
  16. package/dist/canvas/keys.js +133 -0
  17. package/dist/canvas/keys.js.map +1 -0
  18. package/dist/canvas/layout.d.ts +26 -0
  19. package/dist/canvas/layout.d.ts.map +1 -0
  20. package/dist/canvas/layout.js +73 -0
  21. package/dist/canvas/layout.js.map +1 -0
  22. package/dist/canvas/scroll.d.ts +31 -0
  23. package/dist/canvas/scroll.d.ts.map +1 -0
  24. package/dist/canvas/scroll.js +111 -0
  25. package/dist/canvas/scroll.js.map +1 -0
  26. package/dist/canvas/text.d.ts +23 -0
  27. package/dist/canvas/text.d.ts.map +1 -0
  28. package/dist/canvas/text.js +142 -0
  29. package/dist/canvas/text.js.map +1 -0
  30. package/dist/{theme.d.ts → canvas/theme.d.ts} +8 -0
  31. package/dist/canvas/theme.d.ts.map +1 -0
  32. package/dist/canvas/theme.js +62 -0
  33. package/dist/canvas/theme.js.map +1 -0
  34. package/dist/core/cache.d.ts +48 -0
  35. package/dist/core/cache.d.ts.map +1 -0
  36. package/dist/core/cache.js +222 -0
  37. package/dist/core/cache.js.map +1 -0
  38. package/dist/core/checked.d.ts +20 -0
  39. package/dist/core/checked.d.ts.map +1 -0
  40. package/dist/core/checked.js +61 -0
  41. package/dist/core/checked.js.map +1 -0
  42. package/dist/core/client.d.ts +33 -0
  43. package/dist/core/client.d.ts.map +1 -0
  44. package/dist/core/client.js +103 -0
  45. package/dist/core/client.js.map +1 -0
  46. package/dist/core/conditions.d.ts +48 -0
  47. package/dist/core/conditions.d.ts.map +1 -0
  48. package/dist/core/conditions.js +105 -0
  49. package/dist/core/conditions.js.map +1 -0
  50. package/dist/core/evaluate.d.ts +20 -0
  51. package/dist/core/evaluate.d.ts.map +1 -0
  52. package/dist/core/evaluate.js +210 -0
  53. package/dist/core/evaluate.js.map +1 -0
  54. package/dist/core/filters.d.ts +51 -0
  55. package/dist/core/filters.d.ts.map +1 -0
  56. package/dist/core/filters.js +303 -0
  57. package/dist/core/filters.js.map +1 -0
  58. package/dist/core/format.d.ts +14 -0
  59. package/dist/core/format.d.ts.map +1 -0
  60. package/dist/core/format.js +303 -0
  61. package/dist/core/format.js.map +1 -0
  62. package/dist/core/measures.d.ts +26 -0
  63. package/dist/core/measures.d.ts.map +1 -0
  64. package/dist/core/measures.js +105 -0
  65. package/dist/core/measures.js.map +1 -0
  66. package/dist/core/request-ids.d.ts +20 -0
  67. package/dist/core/request-ids.d.ts.map +1 -0
  68. package/dist/core/request-ids.js +55 -0
  69. package/dist/core/request-ids.js.map +1 -0
  70. package/dist/core/rows.d.ts +79 -0
  71. package/dist/core/rows.d.ts.map +1 -0
  72. package/dist/core/rows.js +173 -0
  73. package/dist/core/rows.js.map +1 -0
  74. package/dist/core/store.d.ts +233 -0
  75. package/dist/core/store.d.ts.map +1 -0
  76. package/dist/core/store.js +1162 -0
  77. package/dist/core/store.js.map +1 -0
  78. package/dist/core/totals.d.ts +24 -0
  79. package/dist/core/totals.d.ts.map +1 -0
  80. package/dist/core/totals.js +49 -0
  81. package/dist/core/totals.js.map +1 -0
  82. package/dist/core/trace.d.ts +7 -0
  83. package/dist/core/trace.d.ts.map +1 -0
  84. package/dist/core/trace.js +26 -0
  85. package/dist/core/trace.js.map +1 -0
  86. package/dist/core/transports.d.ts +50 -0
  87. package/dist/core/transports.d.ts.map +1 -0
  88. package/dist/core/transports.js +160 -0
  89. package/dist/core/transports.js.map +1 -0
  90. package/dist/core/types.d.ts +151 -0
  91. package/dist/core/types.d.ts.map +1 -0
  92. package/dist/core/types.js +45 -0
  93. package/dist/core/types.js.map +1 -0
  94. package/dist/core.d.ts +12 -0
  95. package/dist/core.d.ts.map +1 -0
  96. package/dist/core.js +11 -0
  97. package/dist/core.js.map +1 -0
  98. package/dist/index.d.ts +15 -6
  99. package/dist/index.d.ts.map +1 -1
  100. package/dist/index.js +13 -7
  101. package/dist/index.js.map +1 -1
  102. package/dist/react/accessible-rows.d.ts +13 -0
  103. package/dist/react/accessible-rows.d.ts.map +1 -0
  104. package/dist/react/accessible-rows.js +320 -0
  105. package/dist/react/accessible-rows.js.map +1 -0
  106. package/dist/react/cell-editor.d.ts +22 -0
  107. package/dist/react/cell-editor.d.ts.map +1 -0
  108. package/dist/react/cell-editor.js +431 -0
  109. package/dist/react/cell-editor.js.map +1 -0
  110. package/dist/react/cell-menu.d.ts +18 -0
  111. package/dist/react/cell-menu.d.ts.map +1 -0
  112. package/dist/react/cell-menu.js +299 -0
  113. package/dist/react/cell-menu.js.map +1 -0
  114. package/dist/react/column-menu.d.ts +16 -0
  115. package/dist/react/column-menu.d.ts.map +1 -0
  116. package/dist/react/column-menu.js +1311 -0
  117. package/dist/react/column-menu.js.map +1 -0
  118. package/dist/react/columns-menu.d.ts +7 -0
  119. package/dist/react/columns-menu.d.ts.map +1 -0
  120. package/dist/react/columns-menu.js +249 -0
  121. package/dist/react/columns-menu.js.map +1 -0
  122. package/dist/react/conditions-menu.d.ts +10 -0
  123. package/dist/react/conditions-menu.d.ts.map +1 -0
  124. package/dist/react/conditions-menu.js +795 -0
  125. package/dist/react/conditions-menu.js.map +1 -0
  126. package/dist/react/context.d.ts +29 -0
  127. package/dist/react/context.d.ts.map +1 -0
  128. package/dist/react/context.js +18 -0
  129. package/dist/react/context.js.map +1 -0
  130. package/dist/react/data-grid.d.ts +79 -0
  131. package/dist/react/data-grid.d.ts.map +1 -0
  132. package/dist/react/data-grid.js +653 -0
  133. package/dist/react/data-grid.js.map +1 -0
  134. package/dist/react/detail-panel.d.ts +22 -0
  135. package/dist/react/detail-panel.d.ts.map +1 -0
  136. package/dist/react/detail-panel.js +242 -0
  137. package/dist/react/detail-panel.js.map +1 -0
  138. package/dist/react/export-menu.d.ts +11 -0
  139. package/dist/react/export-menu.d.ts.map +1 -0
  140. package/dist/react/export-menu.js +304 -0
  141. package/dist/react/export-menu.js.map +1 -0
  142. package/dist/react/find-bar.d.ts +11 -0
  143. package/dist/react/find-bar.d.ts.map +1 -0
  144. package/dist/react/find-bar.js +303 -0
  145. package/dist/react/find-bar.js.map +1 -0
  146. package/dist/react/format-form.d.ts +12 -0
  147. package/dist/react/format-form.d.ts.map +1 -0
  148. package/dist/react/format-form.js +863 -0
  149. package/dist/react/format-form.js.map +1 -0
  150. package/dist/react/formula-menu.d.ts +6 -0
  151. package/dist/react/formula-menu.d.ts.map +1 -0
  152. package/dist/react/formula-menu.js +343 -0
  153. package/dist/react/formula-menu.js.map +1 -0
  154. package/dist/react/grid-body.d.ts +33 -0
  155. package/dist/react/grid-body.d.ts.map +1 -0
  156. package/dist/react/grid-body.js +1389 -0
  157. package/dist/react/grid-body.js.map +1 -0
  158. package/dist/react/group-menu.d.ts +7 -0
  159. package/dist/react/group-menu.d.ts.map +1 -0
  160. package/dist/react/group-menu.js +508 -0
  161. package/dist/react/group-menu.js.map +1 -0
  162. package/dist/react/hooks.d.ts +33 -0
  163. package/dist/react/hooks.d.ts.map +1 -0
  164. package/dist/react/hooks.js +161 -0
  165. package/dist/react/hooks.js.map +1 -0
  166. package/dist/react/messages.d.ts +180 -0
  167. package/dist/react/messages.d.ts.map +1 -0
  168. package/dist/react/messages.js +414 -0
  169. package/dist/react/messages.js.map +1 -0
  170. package/dist/react/save.d.ts +9 -0
  171. package/dist/react/save.d.ts.map +1 -0
  172. package/dist/react/save.js +102 -0
  173. package/dist/react/save.js.map +1 -0
  174. package/dist/react/static-rows.d.ts +9 -0
  175. package/dist/react/static-rows.d.ts.map +1 -0
  176. package/dist/react/static-rows.js +176 -0
  177. package/dist/react/static-rows.js.map +1 -0
  178. package/dist/react/status-bar.d.ts +12 -0
  179. package/dist/react/status-bar.d.ts.map +1 -0
  180. package/dist/react/status-bar.js +413 -0
  181. package/dist/react/status-bar.js.map +1 -0
  182. package/dist/react/toolbar-popover.d.ts +14 -0
  183. package/dist/react/toolbar-popover.d.ts.map +1 -0
  184. package/dist/react/toolbar-popover.js +143 -0
  185. package/dist/react/toolbar-popover.js.map +1 -0
  186. package/dist/react/toolbar.d.ts +13 -0
  187. package/dist/react/toolbar.d.ts.map +1 -0
  188. package/dist/react/toolbar.js +344 -0
  189. package/dist/react/toolbar.js.map +1 -0
  190. package/dist/server.d.ts +22 -0
  191. package/dist/server.d.ts.map +1 -0
  192. package/dist/server.js +45 -0
  193. package/dist/server.js.map +1 -0
  194. package/dist/styles.css +496 -316
  195. package/package.json +42 -17
  196. package/src/canvas/geometry.ts +197 -0
  197. package/src/canvas/grid-canvas.ts +1222 -0
  198. package/src/canvas/hooks.ts +61 -0
  199. package/src/canvas/keys.ts +96 -0
  200. package/src/canvas/layout.ts +77 -0
  201. package/src/canvas/scroll.ts +120 -0
  202. package/src/canvas/text.ts +155 -0
  203. package/src/{theme.ts → canvas/theme.ts} +14 -1
  204. package/src/core/cache.ts +247 -0
  205. package/src/core/checked.ts +64 -0
  206. package/src/core/client.ts +103 -0
  207. package/src/core/conditions.ts +89 -0
  208. package/src/core/evaluate.ts +221 -0
  209. package/src/core/filters.ts +268 -0
  210. package/src/core/format.ts +309 -0
  211. package/src/core/measures.ts +99 -0
  212. package/src/core/request-ids.ts +54 -0
  213. package/src/core/rows.ts +213 -0
  214. package/src/core/store.ts +1122 -0
  215. package/src/core/totals.ts +59 -0
  216. package/src/core/trace.ts +44 -0
  217. package/src/core/transports.ts +177 -0
  218. package/src/core/types.ts +169 -0
  219. package/src/core.ts +39 -0
  220. package/src/index.ts +24 -12
  221. package/src/react/accessible-rows.tsx +242 -0
  222. package/src/react/cell-editor.tsx +216 -0
  223. package/src/react/cell-menu.tsx +166 -0
  224. package/src/react/column-menu.tsx +619 -0
  225. package/src/react/columns-menu.tsx +116 -0
  226. package/src/react/conditions-menu.tsx +289 -0
  227. package/src/react/context.ts +33 -0
  228. package/src/react/data-grid.tsx +286 -0
  229. package/src/react/detail-panel.tsx +92 -0
  230. package/src/react/export-menu.tsx +112 -0
  231. package/src/react/find-bar.tsx +112 -0
  232. package/src/react/format-form.tsx +421 -0
  233. package/src/react/formula-menu.tsx +163 -0
  234. package/src/react/grid-body.tsx +744 -0
  235. package/src/react/group-menu.tsx +214 -0
  236. package/src/react/hooks.ts +104 -0
  237. package/src/react/messages.ts +556 -0
  238. package/src/react/save.ts +42 -0
  239. package/src/react/static-rows.tsx +59 -0
  240. package/src/react/status-bar.tsx +175 -0
  241. package/src/react/toolbar-popover.tsx +69 -0
  242. package/src/react/toolbar.tsx +152 -0
  243. package/src/server.ts +58 -0
  244. package/src/styles.css +496 -316
  245. package/dist/components/bars.d.ts +0 -14
  246. package/dist/components/bars.d.ts.map +0 -1
  247. package/dist/components/bars.js +0 -68
  248. package/dist/components/bars.js.map +0 -1
  249. package/dist/components/cell-inspector.d.ts +0 -11
  250. package/dist/components/cell-inspector.d.ts.map +0 -1
  251. package/dist/components/cell-inspector.js +0 -37
  252. package/dist/components/cell-inspector.js.map +0 -1
  253. package/dist/components/columns-panel.d.ts +0 -12
  254. package/dist/components/columns-panel.d.ts.map +0 -1
  255. package/dist/components/columns-panel.js +0 -11
  256. package/dist/components/columns-panel.js.map +0 -1
  257. package/dist/components/filter-panel.d.ts +0 -22
  258. package/dist/components/filter-panel.d.ts.map +0 -1
  259. package/dist/components/filter-panel.js +0 -169
  260. package/dist/components/filter-panel.js.map +0 -1
  261. package/dist/components/find-bar.d.ts +0 -9
  262. package/dist/components/find-bar.d.ts.map +0 -1
  263. package/dist/components/find-bar.js +0 -46
  264. package/dist/components/find-bar.js.map +0 -1
  265. package/dist/components/popover.d.ts +0 -38
  266. package/dist/components/popover.d.ts.map +0 -1
  267. package/dist/components/popover.js +0 -104
  268. package/dist/components/popover.js.map +0 -1
  269. package/dist/components/stats-panel.d.ts +0 -12
  270. package/dist/components/stats-panel.d.ts.map +0 -1
  271. package/dist/components/stats-panel.js +0 -40
  272. package/dist/components/stats-panel.js.map +0 -1
  273. package/dist/controller.d.ts +0 -227
  274. package/dist/controller.d.ts.map +0 -1
  275. package/dist/controller.js +0 -845
  276. package/dist/controller.js.map +0 -1
  277. package/dist/data-grid.d.ts +0 -51
  278. package/dist/data-grid.d.ts.map +0 -1
  279. package/dist/data-grid.js +0 -704
  280. package/dist/data-grid.js.map +0 -1
  281. package/dist/labels.d.ts +0 -9
  282. package/dist/labels.d.ts.map +0 -1
  283. package/dist/labels.js +0 -38
  284. package/dist/labels.js.map +0 -1
  285. package/dist/messages.d.ts +0 -99
  286. package/dist/messages.d.ts.map +0 -1
  287. package/dist/messages.js +0 -276
  288. package/dist/messages.js.map +0 -1
  289. package/dist/renderer.d.ts +0 -52
  290. package/dist/renderer.d.ts.map +0 -1
  291. package/dist/renderer.js +0 -522
  292. package/dist/renderer.js.map +0 -1
  293. package/dist/text.d.ts +0 -14
  294. package/dist/text.d.ts.map +0 -1
  295. package/dist/text.js +0 -63
  296. package/dist/text.js.map +0 -1
  297. package/dist/theme.d.ts.map +0 -1
  298. package/dist/theme.js +0 -54
  299. package/dist/theme.js.map +0 -1
  300. package/src/components/bars.tsx +0 -180
  301. package/src/components/cell-inspector.tsx +0 -70
  302. package/src/components/columns-panel.tsx +0 -59
  303. package/src/components/filter-panel.tsx +0 -293
  304. package/src/components/find-bar.tsx +0 -104
  305. package/src/components/popover.tsx +0 -194
  306. package/src/components/stats-panel.tsx +0 -153
  307. package/src/controller.ts +0 -945
  308. package/src/data-grid.tsx +0 -1035
  309. package/src/labels.ts +0 -46
  310. package/src/messages.ts +0 -390
  311. package/src/renderer.ts +0 -535
  312. package/src/text.ts +0 -55
package/dist/data-grid.js DELETED
@@ -1,704 +0,0 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { forwardRef, useCallback, useEffect, useId, useImperativeHandle, useMemo, useRef, useState, useSyncExternalStore, } from "react";
3
- import { plainText } from "vastgrid-core";
4
- import { StatusBar, Toolbar } from "./components/bars.js";
5
- import { CellInspector } from "./components/cell-inspector.js";
6
- import { ColumnsPanel } from "./components/columns-panel.js";
7
- import { FilterPanel } from "./components/filter-panel.js";
8
- import { FindBar } from "./components/find-bar.js";
9
- import { MenuList, Popover } from "./components/popover.js";
10
- import { StatsPanel } from "./components/stats-panel.js";
11
- import { CopyTooLargeError, GridController } from "./controller.js";
12
- import { countFormat, numberFormat } from "./labels.js";
13
- import { messagesFor } from "./messages.js";
14
- import { CanvasRenderer } from "./renderer.js";
15
- import { cssLength, resolveTheme } from "./theme.js";
16
- const isMac = typeof navigator !== "undefined" && /Mac|iPhone|iPad/.test(navigator.platform || navigator.userAgent);
17
- async function writeClipboard(text, html) {
18
- const clipboard = typeof navigator === "undefined" ? undefined : navigator.clipboard;
19
- if (!clipboard)
20
- throw new Error("The clipboard is not available.");
21
- if (clipboard.write && typeof ClipboardItem !== "undefined") {
22
- const items = {
23
- "text/plain": text.then((value) => new Blob([value], { type: "text/plain" })),
24
- };
25
- if (html)
26
- items["text/html"] = html.then((value) => new Blob([value ?? ""], { type: "text/html" }));
27
- await clipboard.write([new ClipboardItem(items)]);
28
- return;
29
- }
30
- await clipboard.writeText(await text);
31
- }
32
- /**
33
- * A data grid for very large tables. Draws on a canvas, reads rows through a `TableSource` as you
34
- * scroll, and offers spreadsheet selection and keyboard navigation, copying, sorting, filters,
35
- * search, column statistics and a selection summary.
36
- */
37
- export const DataGrid = forwardRef(function DataGrid(props, ref) {
38
- const { source, locale, rowHeight, headerHeight, nullText = "", frozenColumns = 0, toolbar = true, statusBar = true, reloadKey, copyLimit = 1_000_000, onQueryChange, className, style, } = props;
39
- const resolvedLocale = locale ?? (typeof navigator === "undefined" ? undefined : navigator.language);
40
- const messages = useMemo(() => ({ ...messagesFor(resolvedLocale), ...props.messages }), [resolvedLocale, props.messages]);
41
- const rootRef = useRef(null);
42
- const bodyRef = useRef(null);
43
- const canvasRef = useRef(null);
44
- const scrollerRef = useRef(null);
45
- const sizerRef = useRef(null);
46
- const rendererRef = useRef(null);
47
- const dragRef = useRef(null);
48
- const autoScrollRef = useRef(0);
49
- const cellId = useId();
50
- const [overlay, setOverlay] = useState(null);
51
- const [findOpen, setFindOpen] = useState(false);
52
- const [notice, setNotice] = useState(null);
53
- const noticeTimer = useRef(undefined);
54
- // One controller per source. Options that can change later are applied in effects below.
55
- // biome-ignore lint/correctness/useExhaustiveDependencies: the other options are applied without recreating the grid.
56
- const controller = useMemo(() => new GridController(source, {
57
- rowHeight: rowHeight ?? 24,
58
- headerHeight: headerHeight ?? 32,
59
- locale: resolvedLocale,
60
- nullText,
61
- frozenColumns,
62
- minColumnWidth: 36,
63
- maxAutoWidth: 360,
64
- copyLimit,
65
- }), [source]);
66
- useSyncExternalStore(controller.subscribe, controller.getVersion, controller.getVersion);
67
- const count = useMemo(() => countFormat(resolvedLocale), [resolvedLocale]);
68
- const number = useMemo(() => numberFormat(resolvedLocale), [resolvedLocale]);
69
- useEffect(() => {
70
- if (resolvedLocale !== controller.options.locale || nullText !== controller.options.nullText) {
71
- controller.setFormat(resolvedLocale, nullText);
72
- }
73
- }, [controller, resolvedLocale, nullText]);
74
- const announce = useCallback((text) => {
75
- setNotice(text);
76
- clearTimeout(noticeTimer.current);
77
- noticeTimer.current = setTimeout(() => setNotice(null), 3000);
78
- }, []);
79
- useEffect(() => () => clearTimeout(noticeTimer.current), []);
80
- // Canvas, scrolling, sizes and theme.
81
- useEffect(() => {
82
- const root = rootRef.current;
83
- const canvas = canvasRef.current;
84
- const scroller = scrollerRef.current;
85
- const sizer = sizerRef.current;
86
- if (!root || !canvas || !scroller || !sizer)
87
- return;
88
- controller.options.rowHeight = rowHeight ?? cssLength(root, "--vg-row-height", 24);
89
- controller.options.headerHeight = headerHeight ?? cssLength(root, "--vg-header-height", 32);
90
- const renderer = new CanvasRenderer(canvas, controller, resolveTheme(root));
91
- rendererRef.current = renderer;
92
- controller.attach(scroller, sizer);
93
- const stopRender = controller.onRender(() => renderer.schedule());
94
- const resize = () => controller.setViewport(scroller.clientWidth, scroller.clientHeight);
95
- resize();
96
- const observer = new ResizeObserver(resize);
97
- observer.observe(scroller);
98
- const onScroll = () => controller.handleScroll();
99
- scroller.addEventListener("scroll", onScroll, { passive: true });
100
- // Without a text selection, Chromium and Safari disable the copy command unless "beforecopy" is
101
- // cancelled; then ⌘C and the Edit menu fire "copy", which the grid answers while it has focus.
102
- const onBeforeCopy = (event) => {
103
- if (document.activeElement === scroller)
104
- event.preventDefault();
105
- };
106
- const onCopy = (event) => {
107
- if (document.activeElement === scroller)
108
- copyHandler.current(event);
109
- };
110
- document.addEventListener("beforecopy", onBeforeCopy);
111
- document.addEventListener("copy", onCopy);
112
- const refreshTheme = () => renderer.setTheme(resolveTheme(root));
113
- const media = window.matchMedia("(prefers-color-scheme: dark)");
114
- media.addEventListener("change", refreshTheme);
115
- const themeObserver = new MutationObserver(refreshTheme);
116
- themeObserver.observe(document.documentElement, {
117
- attributes: true,
118
- attributeFilter: ["class", "style", "data-theme"],
119
- });
120
- void document.fonts?.ready.then(refreshTheme);
121
- controller.start();
122
- renderer.schedule();
123
- return () => {
124
- controller.stop();
125
- stopRender();
126
- observer.disconnect();
127
- scroller.removeEventListener("scroll", onScroll);
128
- document.removeEventListener("beforecopy", onBeforeCopy);
129
- document.removeEventListener("copy", onCopy);
130
- media.removeEventListener("change", refreshTheme);
131
- themeObserver.disconnect();
132
- renderer.dispose();
133
- rendererRef.current = null;
134
- };
135
- }, [controller, rowHeight, headerHeight]);
136
- // Theme changes through the grid's own class or style.
137
- // biome-ignore lint/correctness/useExhaustiveDependencies: className and style are the triggers.
138
- useEffect(() => {
139
- const root = rootRef.current;
140
- if (root && rendererRef.current)
141
- rendererRef.current.setTheme(resolveTheme(root));
142
- }, [className, style]);
143
- const firstReload = useRef(true);
144
- // biome-ignore lint/correctness/useExhaustiveDependencies: reloadKey is the trigger.
145
- useEffect(() => {
146
- if (firstReload.current) {
147
- firstReload.current = false;
148
- return;
149
- }
150
- controller.reload();
151
- }, [reloadKey, controller]);
152
- const lastQuery = useRef(controller.query);
153
- useEffect(() => {
154
- if (lastQuery.current !== controller.query) {
155
- lastQuery.current = controller.query;
156
- onQueryChange?.(controller.query);
157
- }
158
- });
159
- useImperativeHandle(ref, () => ({
160
- focus: () => scrollerRef.current?.focus(),
161
- reload: () => controller.reload(),
162
- getQuery: () => controller.query,
163
- setQuery: (query) => void controller.setQuery(query),
164
- goToRow: (row) => {
165
- controller.selection.select({ row, col: controller.selection.active.col });
166
- controller.selectionChanged({ row, col: controller.selection.active.col });
167
- },
168
- controller,
169
- get renderer() {
170
- return rendererRef.current;
171
- },
172
- }), [controller]);
173
- // ----- helpers -----
174
- function local(event) {
175
- const rect = scrollerRef.current?.getBoundingClientRect();
176
- return { x: event.clientX - (rect?.left ?? 0), y: event.clientY - (rect?.top ?? 0) };
177
- }
178
- /** A popover anchor at a point of the grid body. */
179
- function anchorAt(x, y, height = 0) {
180
- const offsetX = bodyRef.current?.offsetLeft ?? 0;
181
- const offsetY = bodyRef.current?.offsetTop ?? 0;
182
- return { x: x + offsetX, y: y + offsetY, height };
183
- }
184
- function headerAnchor(col) {
185
- const x = Math.max(controller.gutterWidth, controller.columnX(col));
186
- return anchorAt(x, 0, controller.options.headerHeight);
187
- }
188
- function cellAnchor(row, col) {
189
- const x = Math.max(controller.gutterWidth, controller.columnX(col));
190
- const y = Math.max(controller.options.headerHeight, Math.min(controller.height - 8, controller.rowY(row)));
191
- return anchorAt(x, y, controller.options.rowHeight);
192
- }
193
- function focusGrid() {
194
- scrollerRef.current?.focus({ preventScroll: true });
195
- }
196
- const copy = useCallback(async (options) => {
197
- const task = controller.copySelection(options);
198
- try {
199
- await writeClipboard(task.then((result) => result.text), options.markdown ? null : task.then((result) => result.html));
200
- const result = await task;
201
- announce(messages.copied(count(result.cells)));
202
- }
203
- catch (error) {
204
- if (error instanceof CopyTooLargeError)
205
- announce(messages.copyTooLarge(count(error.limit)));
206
- else
207
- announce(messages.copyFailed);
208
- }
209
- }, [announce, controller, count, messages]);
210
- /** Options of a copy started from the keyboard, answered by the browser's own copy event. */
211
- const pendingCopy = useRef(null);
212
- const copyHandler = useRef(() => undefined);
213
- // ----- pointer -----
214
- function selectMode(event) {
215
- if (event.shiftKey)
216
- return "extend";
217
- if (isMac ? event.metaKey : event.ctrlKey)
218
- return "add";
219
- return "replace";
220
- }
221
- function dragTo(x, y) {
222
- const drag = dragRef.current;
223
- if (!drag || (drag.kind !== "cells" && drag.kind !== "rows" && drag.kind !== "cols"))
224
- return;
225
- const c = controller;
226
- const clampedX = Math.min(Math.max(x, c.gutterWidth + 1), c.width - 1);
227
- const clampedY = Math.min(Math.max(y, c.options.headerHeight + 1), c.height - 1);
228
- const col = c.columnAt(clampedX);
229
- const row = c.rowAt(clampedY);
230
- if (drag.kind === "rows" && row >= 0)
231
- c.selection.selectRows(row, row, "extend");
232
- else if (drag.kind === "cols" && col >= 0)
233
- c.selection.selectCols(col, col, "extend");
234
- else if (drag.kind === "cells" && row >= 0 && col >= 0)
235
- c.selection.select({ row, col }, "extend");
236
- c.selectionChanged();
237
- }
238
- function autoScroll() {
239
- const drag = dragRef.current;
240
- if (!drag || (drag.kind !== "cells" && drag.kind !== "rows" && drag.kind !== "cols")) {
241
- autoScrollRef.current = 0;
242
- return;
243
- }
244
- const c = controller;
245
- const speed = (distance) => Math.sign(distance) * Math.min(80, Math.abs(distance) * 0.6);
246
- let dx = 0;
247
- let dy = 0;
248
- if (drag.y < c.options.headerHeight)
249
- dy = speed(drag.y - c.options.headerHeight);
250
- else if (drag.y > c.height)
251
- dy = speed(drag.y - c.height);
252
- if (drag.kind !== "rows") {
253
- if (drag.x < c.gutterWidth + c.frozenWidth && c.scrollX.position > 0)
254
- dx = speed(drag.x - c.gutterWidth - c.frozenWidth);
255
- else if (drag.x > c.width)
256
- dx = speed(drag.x - c.width);
257
- }
258
- if (dx !== 0 || dy !== 0) {
259
- c.scrollToVirtual(dx === 0 ? null : c.scrollX.position + dx, dy === 0 ? null : c.scrollY.position + dy);
260
- dragTo(drag.x, drag.y);
261
- }
262
- autoScrollRef.current = requestAnimationFrame(autoScroll);
263
- }
264
- function onPointerDown(event) {
265
- const scroller = scrollerRef.current;
266
- if (!scroller || event.button !== 0)
267
- return;
268
- const { x, y } = local(event);
269
- // Leave the native scrollbars alone.
270
- if (x > scroller.clientWidth || y > scroller.clientHeight)
271
- return;
272
- focusGrid();
273
- const c = controller;
274
- const hit = c.hitTest(x, y);
275
- const mode = selectMode(event);
276
- if (hit.resize >= 0) {
277
- event.preventDefault();
278
- scroller.setPointerCapture(event.pointerId);
279
- dragRef.current = {
280
- kind: "resize",
281
- pointerId: event.pointerId,
282
- col: hit.resize,
283
- startX: x,
284
- startWidth: c.layout.width(hit.resize),
285
- };
286
- c.resizing = hit.resize;
287
- c.requestRender();
288
- return;
289
- }
290
- if (hit.menu && hit.col >= 0) {
291
- event.preventDefault();
292
- setOverlay({ kind: "columnMenu", col: hit.col, anchor: headerAnchor(hit.col) });
293
- return;
294
- }
295
- switch (hit.zone) {
296
- case "cell":
297
- c.selection.select({ row: hit.row, col: hit.col }, mode);
298
- dragRef.current = { kind: "cells", pointerId: event.pointerId, x, y };
299
- break;
300
- case "header":
301
- if (mode === "replace" && c.selection.colSelected(hit.col)) {
302
- dragRef.current = { kind: "move", pointerId: event.pointerId, col: hit.col, x, y, moved: false };
303
- }
304
- else {
305
- c.selection.selectCols(hit.col, hit.col, mode);
306
- dragRef.current = { kind: "cols", pointerId: event.pointerId, x, y };
307
- }
308
- break;
309
- case "gutter":
310
- c.selection.selectRows(hit.row, hit.row, mode);
311
- dragRef.current = { kind: "rows", pointerId: event.pointerId, x, y };
312
- break;
313
- case "corner":
314
- c.selection.selectAll();
315
- break;
316
- default:
317
- return;
318
- }
319
- event.preventDefault();
320
- scroller.setPointerCapture(event.pointerId);
321
- c.selectionChanged();
322
- }
323
- function onPointerMove(event) {
324
- const scroller = scrollerRef.current;
325
- if (!scroller)
326
- return;
327
- const { x, y } = local(event);
328
- const c = controller;
329
- const drag = dragRef.current;
330
- if (drag && drag.pointerId === event.pointerId) {
331
- if (drag.kind === "resize") {
332
- c.resizeColumn(drag.col, drag.startWidth + (x - drag.startX));
333
- return;
334
- }
335
- if (drag.kind === "move") {
336
- drag.moved = drag.moved || Math.abs(x - drag.x) > 4;
337
- drag.x = x;
338
- drag.y = y;
339
- scroller.style.cursor = drag.moved ? "grabbing" : "";
340
- return;
341
- }
342
- drag.x = x;
343
- drag.y = y;
344
- dragTo(x, y);
345
- if (!autoScrollRef.current)
346
- autoScrollRef.current = requestAnimationFrame(autoScroll);
347
- return;
348
- }
349
- const hit = x > scroller.clientWidth || y > scroller.clientHeight
350
- ? { zone: "outside", row: -1, col: -1, resize: -1, menu: false }
351
- : c.hitTest(x, y);
352
- const previous = c.hover;
353
- c.hover = hit;
354
- scroller.style.cursor = hit.resize >= 0 ? "col-resize" : hit.menu ? "pointer" : "";
355
- if (previous?.zone !== hit.zone || previous.col !== hit.col || previous.menu !== hit.menu)
356
- c.requestRender();
357
- }
358
- function endDrag(event) {
359
- const drag = dragRef.current;
360
- if (!drag || drag.pointerId !== event.pointerId)
361
- return;
362
- dragRef.current = null;
363
- cancelAnimationFrame(autoScrollRef.current);
364
- autoScrollRef.current = 0;
365
- const scroller = scrollerRef.current;
366
- if (scroller?.hasPointerCapture(event.pointerId))
367
- scroller.releasePointerCapture(event.pointerId);
368
- if (scroller)
369
- scroller.style.cursor = "";
370
- const c = controller;
371
- if (drag.kind === "resize") {
372
- c.resizing = -1;
373
- c.requestRender();
374
- }
375
- else if (drag.kind === "move") {
376
- const target = c.columnAt(local(event).x);
377
- if (drag.moved && target >= 0 && target !== drag.col) {
378
- c.moveColumn(drag.col, target);
379
- c.selection.selectCols(target, target);
380
- c.selectionChanged();
381
- }
382
- else if (!drag.moved) {
383
- c.selection.selectCols(drag.col, drag.col);
384
- c.selectionChanged();
385
- }
386
- }
387
- }
388
- function onPointerLeave() {
389
- if (controller.hover && !dragRef.current) {
390
- controller.hover = null;
391
- controller.requestRender();
392
- }
393
- }
394
- function onDoubleClick(event) {
395
- const { x, y } = local(event);
396
- const hit = controller.hitTest(x, y);
397
- if (hit.resize >= 0) {
398
- controller.autoFit(hit.resize);
399
- controller.emit();
400
- }
401
- else if (hit.zone === "cell") {
402
- setOverlay({ kind: "inspector", row: hit.row, col: hit.col, anchor: cellAnchor(hit.row, hit.col) });
403
- }
404
- }
405
- function onContextMenu(event) {
406
- const { x, y } = local(event);
407
- const c = controller;
408
- const hit = c.hitTest(x, y);
409
- if (hit.zone === "header") {
410
- event.preventDefault();
411
- setOverlay({ kind: "columnMenu", col: hit.col, anchor: anchorAt(x, y) });
412
- return;
413
- }
414
- if (hit.zone !== "cell")
415
- return;
416
- event.preventDefault();
417
- if (!c.selection.contains(hit.row, hit.col)) {
418
- c.selection.select({ row: hit.row, col: hit.col });
419
- c.selectionChanged();
420
- }
421
- setOverlay({ kind: "contextMenu", row: hit.row, col: hit.col, anchor: anchorAt(x, y) });
422
- }
423
- // ----- keyboard -----
424
- function onKeyDown(event) {
425
- if (event.target !== scrollerRef.current)
426
- return;
427
- const c = controller;
428
- const primary = isMac ? event.metaKey : event.ctrlKey;
429
- const extend = event.shiftKey;
430
- const lastRow = Math.max(0, c.rowCount - 1);
431
- const lastCol = Math.max(0, c.visible.length - 1);
432
- const focus = extend ? c.selection.focus : c.selection.active;
433
- let target = null;
434
- switch (event.key) {
435
- case "ArrowDown":
436
- if (event.altKey) {
437
- setOverlay({ kind: "columnMenu", col: c.selection.active.col, anchor: headerAnchor(c.selection.active.col) });
438
- break;
439
- }
440
- target = c.selection.moveTo({ row: primary ? lastRow : focus.row + 1, col: focus.col }, extend);
441
- break;
442
- case "ArrowUp":
443
- target = c.selection.moveTo({ row: primary ? 0 : focus.row - 1, col: focus.col }, extend);
444
- break;
445
- case "ArrowRight":
446
- target = c.selection.moveTo({ row: focus.row, col: primary ? lastCol : focus.col + 1 }, extend);
447
- break;
448
- case "ArrowLeft":
449
- target = c.selection.moveTo({ row: focus.row, col: primary ? 0 : focus.col - 1 }, extend);
450
- break;
451
- case "PageDown":
452
- target = event.altKey
453
- ? c.selection.moveTo({ row: focus.row, col: focus.col + 5 }, extend)
454
- : c.selection.moveTo({ row: focus.row + c.pageRows, col: focus.col }, extend);
455
- break;
456
- case "PageUp":
457
- target = event.altKey
458
- ? c.selection.moveTo({ row: focus.row, col: focus.col - 5 }, extend)
459
- : c.selection.moveTo({ row: focus.row - c.pageRows, col: focus.col }, extend);
460
- break;
461
- case "Home":
462
- target = c.selection.moveTo({ row: event.ctrlKey || event.metaKey ? 0 : focus.row, col: 0 }, extend);
463
- break;
464
- case "End":
465
- target = c.selection.moveTo({ row: event.ctrlKey || event.metaKey ? lastRow : focus.row, col: lastCol }, extend);
466
- break;
467
- case "Tab":
468
- target = c.selection.advance(0, event.shiftKey ? -1 : 1);
469
- break;
470
- case "Enter":
471
- target = c.selection.advance(event.shiftKey ? -1 : 1, 0);
472
- break;
473
- case " ":
474
- if (event.shiftKey)
475
- c.selection.selectRows(c.selection.active.row, c.selection.active.row);
476
- else if (event.ctrlKey)
477
- c.selection.selectCols(c.selection.active.col, c.selection.active.col);
478
- else
479
- return;
480
- c.selectionChanged();
481
- break;
482
- case "F2":
483
- setOverlay({
484
- kind: "inspector",
485
- ...c.selection.active,
486
- anchor: cellAnchor(c.selection.active.row, c.selection.active.col),
487
- });
488
- break;
489
- case "Escape":
490
- if (findOpen)
491
- setFindOpen(false);
492
- else if (!c.selection.single) {
493
- c.selection.select(c.selection.active);
494
- c.selectionChanged();
495
- }
496
- else
497
- return;
498
- break;
499
- case "ContextMenu":
500
- setOverlay({
501
- kind: "contextMenu",
502
- ...c.selection.active,
503
- anchor: cellAnchor(c.selection.active.row, c.selection.active.col),
504
- });
505
- break;
506
- default:
507
- if (primary && event.key.toLowerCase() === "a") {
508
- c.selection.selectAll();
509
- c.selectionChanged();
510
- }
511
- else if (primary && event.key.toLowerCase() === "c") {
512
- // Let the browser run its own copy command: it needs no clipboard permission, and onCopy answers
513
- // it with the loaded cells. Shortcuts the browser does not treat as copying fall back below.
514
- const options = { headers: event.shiftKey };
515
- pendingCopy.current = options;
516
- setTimeout(() => {
517
- if (pendingCopy.current !== options)
518
- return;
519
- pendingCopy.current = null;
520
- void copy(options);
521
- }, 0);
522
- return;
523
- }
524
- else if (primary && event.key.toLowerCase() === "f") {
525
- setFindOpen(true);
526
- }
527
- else if (event.shiftKey && event.key === "F10") {
528
- setOverlay({
529
- kind: "contextMenu",
530
- ...c.selection.active,
531
- anchor: cellAnchor(c.selection.active.row, c.selection.active.col),
532
- });
533
- }
534
- else {
535
- return;
536
- }
537
- }
538
- event.preventDefault();
539
- if (target)
540
- c.selectionChanged(target);
541
- }
542
- /** The browser's own copy command (Edit menu): answer synchronously when the cells are loaded. */
543
- /**
544
- * The browser's copy command (⌘C, or Copy in the app's Edit menu) while the grid has focus. Loaded
545
- * cells are written here, synchronously, which needs no clipboard permission; larger selections are
546
- * read first and written with the asynchronous clipboard API.
547
- */
548
- copyHandler.current = (event) => {
549
- if (!event.clipboardData)
550
- return;
551
- event.preventDefault();
552
- const options = pendingCopy.current ?? {};
553
- pendingCopy.current = null;
554
- const result = controller.copySelectionSync(options);
555
- if (result) {
556
- event.clipboardData.setData("text/plain", result.text);
557
- if (result.html)
558
- event.clipboardData.setData("text/html", result.html);
559
- announce(messages.copied(count(result.cells)));
560
- }
561
- else {
562
- void copy(options);
563
- }
564
- };
565
- // ----- menus -----
566
- const c = controller;
567
- const capabilities = c.info?.capabilities;
568
- const ready = c.info?.status === "ready";
569
- function columnMenuItems(col) {
570
- const state = c.visible[col];
571
- const schema = c.columnSchema(col);
572
- if (!state || !schema)
573
- return [];
574
- const sort = c.sortOf(state.index);
575
- const otherSorts = (c.query.sort ?? []).filter((key) => key.column !== state.index).length > 0;
576
- const items = [
577
- {
578
- label: messages.sortAscending,
579
- onSelect: () => c.sortBy(col, "asc"),
580
- disabled: !ready || !capabilities?.sort,
581
- checked: sort?.direction === "asc" && !otherSorts,
582
- },
583
- {
584
- label: messages.sortDescending,
585
- onSelect: () => c.sortBy(col, "desc"),
586
- disabled: !ready || !capabilities?.sort,
587
- checked: sort?.direction === "desc" && !otherSorts,
588
- },
589
- ];
590
- if ((c.query.sort?.length ?? 0) > 0 && !(sort && !otherSorts)) {
591
- items.push({ label: messages.addSortAscending, onSelect: () => c.sortBy(col, "asc", true), disabled: !ready }, { label: messages.addSortDescending, onSelect: () => c.sortBy(col, "desc", true), disabled: !ready });
592
- }
593
- if ((c.query.sort?.length ?? 0) > 0)
594
- items.push({ label: messages.clearSort, onSelect: () => c.clearSort() });
595
- items.push({
596
- label: messages.filter,
597
- separator: true,
598
- disabled: !ready || !capabilities?.filter,
599
- onSelect: () => setOverlay({ kind: "filter", column: state.index, anchor: headerAnchor(col) }),
600
- }, {
601
- label: messages.statistics,
602
- disabled: !ready || !capabilities?.stats,
603
- onSelect: () => setOverlay({ kind: "stats", col, anchor: headerAnchor(col) }),
604
- }, {
605
- label: messages.autoFit,
606
- separator: true,
607
- onSelect: () => {
608
- c.autoFit(col);
609
- c.emit();
610
- },
611
- }, col < c.frozen && c.frozen > 0
612
- ? { label: messages.unfreeze, onSelect: () => c.freezeUpTo(-1) }
613
- : { label: messages.freezeUpTo, onSelect: () => c.freezeUpTo(col) }, { label: messages.hideColumn, disabled: c.visible.length <= 1, onSelect: () => c.hideColumn(col) }, {
614
- label: messages.copyColumnName,
615
- onSelect: () => void writeClipboard(Promise.resolve(schema.name), null).then(() => announce(messages.copied("1")), (error) => announce(error instanceof Error ? error.message : String(error))),
616
- });
617
- return items;
618
- }
619
- function contextMenuItems(row, col) {
620
- const state = c.visible[col];
621
- const value = c.value(row, col);
622
- const items = [
623
- { label: messages.copy, shortcut: isMac ? "⌘C" : "Ctrl+C", onSelect: () => void copy({}) },
624
- {
625
- label: messages.copyWithHeaders,
626
- shortcut: isMac ? "⇧⌘C" : "Ctrl+Shift+C",
627
- onSelect: () => void copy({ headers: true }),
628
- },
629
- { label: messages.copyAsMarkdown, onSelect: () => void copy({ markdown: true }) },
630
- ];
631
- if (state && value !== undefined) {
632
- items.push({
633
- label: messages.filterToValue,
634
- separator: true,
635
- disabled: !ready || !capabilities?.filter,
636
- onSelect: () => void c.filterByValue(row, col),
637
- }, {
638
- label: messages.excludeValue,
639
- disabled: !ready || !capabilities?.filter,
640
- onSelect: () => void c.filterByValue(row, col, true),
641
- });
642
- }
643
- items.push({
644
- label: messages.sortAscending,
645
- separator: true,
646
- disabled: !ready || !capabilities?.sort,
647
- onSelect: () => c.sortBy(col, "asc"),
648
- }, {
649
- label: messages.sortDescending,
650
- disabled: !ready || !capabilities?.sort,
651
- onSelect: () => c.sortBy(col, "desc"),
652
- }, {
653
- label: messages.inspectCell,
654
- separator: true,
655
- shortcut: "F2",
656
- onSelect: () => setOverlay({ kind: "inspector", row, col, anchor: cellAnchor(row, col) }),
657
- });
658
- return items;
659
- }
660
- const closeOverlay = useCallback(() => setOverlay(null), []);
661
- const loadDistinct = useCallback((search, signal) => overlay?.kind === "filter" ? controller.distinct(overlay.column, search, signal) : null, [controller, overlay]);
662
- const loadStats = useCallback((signal) => (overlay?.kind === "stats" ? controller.stats(overlay.col, signal) : null), [controller, overlay]);
663
- const loadCell = useCallback((signal) => overlay?.kind === "inspector" ? controller.fullValue(overlay.row, overlay.col, signal) : Promise.resolve(null), [controller, overlay]);
664
- // ----- accessibility -----
665
- const active = c.selection.active;
666
- const activeSchema = c.columnSchema(active.col);
667
- const activeValue = c.value(active.row, active.col);
668
- const activeText = activeValue === undefined ? messages.loading : activeValue === null ? messages.blank : plainText(activeValue);
669
- let overlayMessage = null;
670
- if (c.loadError)
671
- overlayMessage = `${messages.error} ${c.loadError}`;
672
- else if (c.info?.status === "error")
673
- overlayMessage = `${messages.error} ${c.info.error ?? ""}`;
674
- else if (c.viewError)
675
- overlayMessage = c.viewError;
676
- else if (!c.info || (c.info.status === "loading" && c.readableRows === 0))
677
- overlayMessage = messages.loading;
678
- else if (c.rowCount === 0 && !c.viewPending)
679
- overlayMessage = messages.noRows;
680
- // Retrying reads the table again after a load error, and builds the view again after a view error.
681
- const retry = c.loadError || c.info?.status === "error"
682
- ? () => controller.reload()
683
- : c.viewError
684
- ? () => void controller.setQuery(controller.query)
685
- : null;
686
- return (_jsxs("div", { ref: rootRef, className: `vg-root${className ? ` ${className}` : ""}`, style: style, "data-status": c.status, children: [toolbar && (_jsx(Toolbar, { controller: c, messages: messages, onColumns: (button) => {
687
- const root = rootRef.current?.getBoundingClientRect();
688
- const rect = button.getBoundingClientRect();
689
- setOverlay({
690
- kind: "columns",
691
- anchor: { x: rect.right - (root?.left ?? 0) - 260, y: rect.top - (root?.top ?? 0), height: rect.height },
692
- });
693
- } })), (c.viewPending || c.info?.status === "loading") && (_jsx("div", { className: "vg-progress", role: "progressbar", "aria-label": messages.loading, "aria-valuemin": 0, "aria-valuemax": 100, "aria-valuenow": c.info?.progress ? Math.round(c.info.progress * 100) : undefined, children: _jsx("span", { style: c.info?.progress && !c.viewPending ? { width: `${Math.round(c.info.progress * 100)}%` } : undefined, "data-indeterminate": c.viewPending || !c.info?.progress || undefined }) })), _jsxs("div", { ref: bodyRef, className: "vg-body", children: [_jsx("canvas", { ref: canvasRef, className: "vg-canvas" }), _jsxs("div", { ref: scrollerRef, className: "vg-scroller", role: "grid", tabIndex: 0, "aria-label": props["aria-label"] ?? messages.gridLabel, "aria-rowcount": c.rowCount + 1, "aria-colcount": c.visible.length + 1, "aria-multiselectable": "true", "aria-activedescendant": c.rowCount > 0 && activeSchema ? cellId : undefined, onPointerDown: onPointerDown, onPointerMove: onPointerMove, onPointerUp: endDrag, onPointerCancel: endDrag, onPointerLeave: onPointerLeave, onDoubleClick: onDoubleClick, onContextMenu: onContextMenu, onKeyDown: onKeyDown, children: [_jsx("div", { ref: sizerRef, className: "vg-sizer" }), activeSchema && c.rowCount > 0 && (_jsxs("div", { className: "vg-sr-only", role: "rowgroup", children: [_jsx("div", { role: "row", "aria-rowindex": 1, children: _jsx("div", { role: "columnheader", "aria-colindex": active.col + 2, children: activeSchema.name }) }), _jsx("div", { role: "row", "aria-rowindex": active.row + 2, children: _jsx("div", { role: "gridcell", id: cellId, "aria-colindex": active.col + 2, "aria-selected": "true", children: activeText }) })] }))] }), overlayMessage && (_jsxs("div", { className: "vg-message", role: c.loadError || c.viewError || c.info?.status === "error" ? "alert" : "status", children: [_jsx("span", { children: overlayMessage }), retry && (_jsx("button", { type: "button", className: "vg-button", onClick: retry, children: messages.retry }))] })), findOpen && (_jsx(FindBar, { messages: messages, initial: c.findText, onFind: (text, direction, caseSensitive, signal) => controller.find(text, direction, caseSensitive, signal), onClose: () => {
694
- setFindOpen(false);
695
- controller.findText = "";
696
- controller.requestRender();
697
- focusGrid();
698
- } })), notice && (_jsx("div", { className: "vg-toast", role: "status", children: notice }))] }), statusBar && _jsx(StatusBar, { controller: c, messages: messages }), overlay?.kind === "columnMenu" && (_jsx(Popover, { anchor: overlay.anchor, container: rootRef, onClose: closeOverlay, returnFocus: scrollerRef, label: c.columnSchema(overlay.col)?.name ?? "", role: "menu", children: _jsx(MenuList, { items: columnMenuItems(overlay.col), onClose: closeOverlay }) })), overlay?.kind === "contextMenu" && (_jsx(Popover, { anchor: overlay.anchor, container: rootRef, onClose: closeOverlay, returnFocus: scrollerRef, label: messages.copy, role: "menu", children: _jsx(MenuList, { items: contextMenuItems(overlay.row, overlay.col), onClose: closeOverlay }) })), overlay?.kind === "filter" && c.schema[overlay.column] && (_jsx(Popover, { anchor: overlay.anchor, container: rootRef, onClose: closeOverlay, returnFocus: scrollerRef, label: messages.filterBy(c.schema[overlay.column].name), className: "vg-popover--panel", children: _jsx(FilterPanel, { column: overlay.column, schema: c.schema[overlay.column], filter: c.filterOf(overlay.column), messages: messages, formatter: c.formatter, count: count, load: loadDistinct, onApply: (filter) => {
699
- c.setFilter(overlay.column, filter);
700
- setOverlay(null);
701
- focusGrid();
702
- } }) })), overlay?.kind === "stats" && c.columnSchema(overlay.col) && (_jsx(Popover, { anchor: overlay.anchor, container: rootRef, onClose: closeOverlay, returnFocus: scrollerRef, label: messages.statistics, className: "vg-popover--panel", children: _jsx(StatsPanel, { schema: c.columnSchema(overlay.col), messages: messages, formatter: c.formatter, count: count, number: number, load: loadStats }) })), overlay?.kind === "inspector" && c.columnSchema(overlay.col) && (_jsx(Popover, { anchor: overlay.anchor, container: rootRef, onClose: closeOverlay, returnFocus: scrollerRef, label: messages.inspectCell, className: "vg-popover--panel vg-popover--wide", children: _jsx(CellInspector, { schema: c.columnSchema(overlay.col), messages: messages, location: messages.cell(count(overlay.row + 1), c.columnSchema(overlay.col)?.name ?? ""), load: loadCell, onCopy: (text) => void writeClipboard(Promise.resolve(text), null).then(() => announce(messages.copied("1")), (error) => announce(error instanceof Error ? error.message : String(error))) }) })), overlay?.kind === "columns" && (_jsx(Popover, { anchor: overlay.anchor, container: rootRef, onClose: closeOverlay, label: messages.columns, className: "vg-popover--panel", children: _jsx(ColumnsPanel, { columns: c.columns, schema: c.schema, messages: messages, onToggle: (index, hidden) => c.setColumnHidden(index, hidden), onShowAll: () => c.showAllColumns() }) }))] }));
703
- });
704
- //# sourceMappingURL=data-grid.js.map