vastgrid 0.1.1 → 0.2.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 (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 +150 -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 +168 -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/theme.js DELETED
@@ -1,54 +0,0 @@
1
- const colorTokens = {
2
- background: "--vg-bg",
3
- text: "--vg-fg",
4
- muted: "--vg-muted",
5
- headerBackground: "--vg-header-bg",
6
- headerText: "--vg-header-fg",
7
- headerActive: "--vg-header-active-bg",
8
- gutterBackground: "--vg-gutter-bg",
9
- gutterText: "--vg-gutter-fg",
10
- gutterViewText: "--vg-gutter-view-fg",
11
- line: "--vg-line",
12
- lineStrong: "--vg-line-strong",
13
- selection: "--vg-selection-bg",
14
- selectionBorder: "--vg-selection-border",
15
- activeBorder: "--vg-active-border",
16
- accent: "--vg-accent",
17
- nullText: "--vg-null-fg",
18
- placeholder: "--vg-placeholder",
19
- match: "--vg-match-bg",
20
- error: "--vg-error-fg",
21
- };
22
- /** Resolves the theme of `root`. Colors given as `light-dark()` or system colors resolve to concrete values. */
23
- export function resolveTheme(root) {
24
- const probe = document.createElement("span");
25
- probe.style.display = "none";
26
- root.appendChild(probe);
27
- try {
28
- const color = (token) => {
29
- probe.style.color = `var(${token})`;
30
- return getComputedStyle(probe).color;
31
- };
32
- const style = getComputedStyle(root);
33
- const fontFamily = style.getPropertyValue("--vg-font-family").trim() || style.fontFamily || "system-ui, sans-serif";
34
- const fontSize = Number.parseFloat(style.getPropertyValue("--vg-font-size")) || Number.parseFloat(style.fontSize) || 13;
35
- const colors = Object.fromEntries(Object.entries(colorTokens).map(([key, token]) => [key, color(token)]));
36
- return {
37
- ...colors,
38
- fontFamily,
39
- fontSize,
40
- font: `${fontSize}px ${fontFamily}`,
41
- headerFont: `600 ${fontSize}px ${fontFamily}`,
42
- smallFont: `${Math.max(9, fontSize - 2)}px ${fontFamily}`,
43
- };
44
- }
45
- finally {
46
- probe.remove();
47
- }
48
- }
49
- /** Reads a length custom property in pixels, or `fallback`. */
50
- export function cssLength(root, token, fallback) {
51
- const value = Number.parseFloat(getComputedStyle(root).getPropertyValue(token));
52
- return Number.isFinite(value) && value > 0 ? value : fallback;
53
- }
54
- //# sourceMappingURL=theme.js.map
package/dist/theme.js.map DELETED
@@ -1 +0,0 @@
1
- {"version":3,"file":"theme.js","sourceRoot":"","sources":["../src/theme.ts"],"names":[],"mappings":"AA4BA,MAAM,WAAW,GAGb;IACF,UAAU,EAAE,SAAS;IACrB,IAAI,EAAE,SAAS;IACf,KAAK,EAAE,YAAY;IACnB,gBAAgB,EAAE,gBAAgB;IAClC,UAAU,EAAE,gBAAgB;IAC5B,YAAY,EAAE,uBAAuB;IACrC,gBAAgB,EAAE,gBAAgB;IAClC,UAAU,EAAE,gBAAgB;IAC5B,cAAc,EAAE,qBAAqB;IACrC,IAAI,EAAE,WAAW;IACjB,UAAU,EAAE,kBAAkB;IAC9B,SAAS,EAAE,mBAAmB;IAC9B,eAAe,EAAE,uBAAuB;IACxC,YAAY,EAAE,oBAAoB;IAClC,MAAM,EAAE,aAAa;IACrB,QAAQ,EAAE,cAAc;IACxB,WAAW,EAAE,kBAAkB;IAC/B,KAAK,EAAE,eAAe;IACtB,KAAK,EAAE,eAAe;CACvB,CAAC;AAEF,gHAAgH;AAChH,MAAM,UAAU,YAAY,CAAC,IAAiB;IAC5C,MAAM,KAAK,GAAG,QAAQ,CAAC,aAAa,CAAC,MAAM,CAAC,CAAC;IAC7C,KAAK,CAAC,KAAK,CAAC,OAAO,GAAG,MAAM,CAAC;IAC7B,IAAI,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC;IACxB,IAAI,CAAC;QACH,MAAM,KAAK,GAAG,CAAC,KAAa,EAAE,EAAE;YAC9B,KAAK,CAAC,KAAK,CAAC,KAAK,GAAG,OAAO,KAAK,GAAG,CAAC;YACpC,OAAO,gBAAgB,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC;QACvC,CAAC,CAAC;QACF,MAAM,KAAK,GAAG,gBAAgB,CAAC,IAAI,CAAC,CAAC;QACrC,MAAM,UAAU,GAAG,KAAK,CAAC,gBAAgB,CAAC,kBAAkB,CAAC,CAAC,IAAI,EAAE,IAAI,KAAK,CAAC,UAAU,IAAI,uBAAuB,CAAC;QACpH,MAAM,QAAQ,GACZ,MAAM,CAAC,UAAU,CAAC,KAAK,CAAC,gBAAgB,CAAC,gBAAgB,CAAC,CAAC,IAAI,MAAM,CAAC,UAAU,CAAC,KAAK,CAAC,QAAQ,CAAC,IAAI,EAAE,CAAC;QACzG,MAAM,MAAM,GAAG,MAAM,CAAC,WAAW,CAAC,MAAM,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,EAAE,KAAK,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,EAAE,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAGvG,CAAC;QACF,OAAO;YACL,GAAG,MAAM;YACT,UAAU;YACV,QAAQ;YACR,IAAI,EAAE,GAAG,QAAQ,MAAM,UAAU,EAAE;YACnC,UAAU,EAAE,OAAO,QAAQ,MAAM,UAAU,EAAE;YAC7C,SAAS,EAAE,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,QAAQ,GAAG,CAAC,CAAC,MAAM,UAAU,EAAE;SAC1D,CAAC;IACJ,CAAC;YAAS,CAAC;QACT,KAAK,CAAC,MAAM,EAAE,CAAC;IACjB,CAAC;AACH,CAAC;AAED,+DAA+D;AAC/D,MAAM,UAAU,SAAS,CAAC,IAAiB,EAAE,KAAa,EAAE,QAAgB;IAC1E,MAAM,KAAK,GAAG,MAAM,CAAC,UAAU,CAAC,gBAAgB,CAAC,IAAI,CAAC,CAAC,gBAAgB,CAAC,KAAK,CAAC,CAAC,CAAC;IAChF,OAAO,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,KAAK,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,QAAQ,CAAC;AAChE,CAAC"}
@@ -1,180 +0,0 @@
1
- import { useEffect, useRef, useState } from "react";
2
- import { filtersRows } from "vastgrid-core";
3
- import type { GridController } from "../controller.js";
4
- import { countFormat, filterLabel, numberFormat } from "../labels.js";
5
- import type { Messages } from "../messages.js";
6
-
7
- /** Search box, active sort and filters, row count and the columns button. */
8
- export function Toolbar({
9
- controller,
10
- messages,
11
- onColumns,
12
- }: {
13
- controller: GridController;
14
- messages: Messages;
15
- onColumns: (button: HTMLButtonElement) => void;
16
- }) {
17
- const query = controller.query;
18
- const [search, setSearch] = useState(query.search ?? "");
19
- const timer = useRef<ReturnType<typeof setTimeout> | undefined>(undefined);
20
- const applied = query.search ?? "";
21
- const lastApplied = useRef(applied);
22
- // Follow changes made elsewhere (for example "Clear all").
23
- if (applied !== lastApplied.current) {
24
- lastApplied.current = applied;
25
- if (search !== applied) setSearch(applied);
26
- }
27
- useEffect(() => () => clearTimeout(timer.current), []);
28
-
29
- const ready = controller.info?.status === "ready";
30
- const capabilities = controller.info?.capabilities;
31
- const count = countFormat(controller.options.locale);
32
- const filters = query.filters ?? [];
33
- const sort = query.sort ?? [];
34
- const total = controller.totalRows;
35
- const filtered = controller.view.id !== null && filtersRows(query);
36
- let rows: string;
37
- if (controller.info?.status === "loading") {
38
- const progress = controller.info.progress;
39
- rows =
40
- progress !== null && progress !== undefined && progress > 0
41
- ? messages.loadingProgress(`${Math.round(progress * 100)}%`)
42
- : messages.loadingRows(count(controller.readableRows));
43
- } else if (filtered && total !== null) {
44
- rows = messages.rowsOf(count(controller.rowCount), count(total));
45
- } else {
46
- rows = messages.rows(count(controller.rowCount));
47
- }
48
-
49
- function change(value: string) {
50
- setSearch(value);
51
- clearTimeout(timer.current);
52
- timer.current = setTimeout(() => controller.setSearch(value), 350);
53
- }
54
-
55
- return (
56
- <div className="vg-toolbar">
57
- <input
58
- type="search"
59
- className="vg-input vg-toolbar__search"
60
- placeholder={messages.searchPlaceholder}
61
- aria-label={messages.search}
62
- value={search}
63
- disabled={!ready || !capabilities?.search}
64
- onChange={(event) => change(event.target.value)}
65
- onKeyDown={(event) => {
66
- if (event.key === "Enter") {
67
- clearTimeout(timer.current);
68
- controller.setSearch(search);
69
- }
70
- }}
71
- />
72
- {sort.length > 0 && (
73
- <span className="vg-chip">
74
- <span>
75
- {messages.sortedBy(
76
- sort
77
- .map(
78
- (key) =>
79
- `${controller.schema[key.column]?.name ?? key.column} ${key.direction === "asc" ? "↑" : "↓"}`,
80
- )
81
- .join(", "),
82
- )}
83
- </span>
84
- <button
85
- type="button"
86
- className="vg-chip__remove"
87
- aria-label={messages.clearSort}
88
- title={messages.clearSort}
89
- onClick={() => controller.clearSort()}
90
- >
91
- ×
92
- </button>
93
- </span>
94
- )}
95
- {filters.map((filter) => {
96
- const label = filterLabel(filter, controller.schema[filter.column], controller.formatter, messages);
97
- return (
98
- <span className="vg-chip vg-chip--filter" key={filter.column}>
99
- <span title={label}>{label}</span>
100
- <button
101
- type="button"
102
- className="vg-chip__remove"
103
- aria-label={messages.removeFilter(label)}
104
- title={messages.removeFilter(label)}
105
- onClick={() => controller.setFilter(filter.column, null)}
106
- >
107
- ×
108
- </button>
109
- </span>
110
- );
111
- })}
112
- {filtersRows(query) && (
113
- <button type="button" className="vg-button vg-button--small" onClick={() => controller.clearFilters()}>
114
- {messages.clearFilters}
115
- </button>
116
- )}
117
- <span className="vg-toolbar__spacer" />
118
- <span className="vg-toolbar__count" aria-live="polite">
119
- {controller.viewPending ? messages.buildingView : rows}
120
- </span>
121
- <button
122
- type="button"
123
- className="vg-button vg-button--small"
124
- onClick={(event) => onColumns(event.currentTarget)}
125
- disabled={!controller.info}
126
- >
127
- {messages.columns}
128
- </button>
129
- </div>
130
- );
131
- }
132
-
133
- /** The active cell and, for multi-cell selections, count, sum, average, minimum and maximum. */
134
- export function StatusBar({ controller, messages }: { controller: GridController; messages: Messages }) {
135
- const count = countFormat(controller.options.locale);
136
- const number = numberFormat(controller.options.locale);
137
- const active = controller.selection.active;
138
- const schema = controller.columnSchema(active.col);
139
- const summary = controller.summary;
140
- const label = controller.rowLabel(active.row);
141
- const notes = (controller.info?.notes ?? []).map((note) => messages.notes[note] ?? note);
142
- return (
143
- <div className="vg-statusbar">
144
- {controller.rowCount > 0 && schema && (
145
- <span className="vg-statusbar__cell">
146
- {messages.cell(count(active.row + 1), schema.name)}
147
- {controller.view.id !== null && label !== null && (
148
- <span className="vg-statusbar__muted"> · {messages.originalRow(count(label))}</span>
149
- )}
150
- </span>
151
- )}
152
- {notes.length > 0 && <span className="vg-statusbar__note">{notes.join(" ")}</span>}
153
- <span className="vg-toolbar__spacer" />
154
- {summary && (
155
- <span className="vg-statusbar__summary">
156
- <span>
157
- {messages.summary.count}: {count(summary.count)}
158
- </span>
159
- {summary.numericCount > 0 && summary.sum !== null && (
160
- <>
161
- <span>
162
- {messages.summary.average}: {number(summary.average ?? 0)}
163
- </span>
164
- <span>
165
- {messages.summary.min}: {number(summary.min ?? 0)}
166
- </span>
167
- <span>
168
- {messages.summary.max}: {number(summary.max ?? 0)}
169
- </span>
170
- <span>
171
- {messages.summary.sum}: {number(summary.sum)}
172
- </span>
173
- </>
174
- )}
175
- </span>
176
- )}
177
- {!summary && controller.summaryPending && <span className="vg-statusbar__muted">…</span>}
178
- </div>
179
- );
180
- }
@@ -1,70 +0,0 @@
1
- import { useEffect, useState } from "react";
2
- import { type CellValue, type ColumnSchema, isAbortError, plainText } from "vastgrid-core";
3
- import type { Messages } from "../messages.js";
4
-
5
- /** Pretty-prints JSON text; returns null when the text is not a JSON object or array. */
6
- function prettyJson(text: string) {
7
- const trimmed = text.trim();
8
- if (!(trimmed.startsWith("{") || trimmed.startsWith("["))) return null;
9
- try {
10
- return JSON.stringify(JSON.parse(trimmed), null, 2);
11
- } catch {
12
- return null;
13
- }
14
- }
15
-
16
- /** The full value of a cell, with line breaks, JSON formatting and a copy button. */
17
- export function CellInspector({
18
- schema,
19
- messages,
20
- location,
21
- load,
22
- onCopy,
23
- }: {
24
- schema: ColumnSchema;
25
- messages: Messages;
26
- location: string;
27
- load: (signal: AbortSignal) => Promise<CellValue>;
28
- onCopy: (text: string) => void;
29
- }) {
30
- const [value, setValue] = useState<{ text: string; isNull: boolean } | null>(null);
31
- const [error, setError] = useState<string | null>(null);
32
-
33
- useEffect(() => {
34
- const controller = new AbortController();
35
- load(controller.signal)
36
- .then((result) => {
37
- if (!controller.signal.aborted) setValue({ text: plainText(result), isNull: result === null });
38
- })
39
- .catch((cause: unknown) => {
40
- if (controller.signal.aborted || isAbortError(cause)) return;
41
- setError(cause instanceof Error ? cause.message : String(cause));
42
- });
43
- return () => controller.abort();
44
- }, [load]);
45
-
46
- const json = value ? prettyJson(value.text) : null;
47
- return (
48
- <div className="vg-inspector">
49
- <p className="vg-panel__title">
50
- {schema.name}
51
- <span className="vg-panel__muted">
52
- {" "}
53
- · {location} · {messages.types[schema.type] ?? schema.type}
54
- </span>
55
- </p>
56
- {error && <p className="vg-panel__error">{error}</p>}
57
- {!value && !error && <p className="vg-panel__muted">{messages.loading}</p>}
58
- {value && (
59
- <pre className="vg-inspector__value" data-null={value.isNull || undefined}>
60
- {value.isNull ? "NULL" : (json ?? value.text)}
61
- </pre>
62
- )}
63
- <div className="vg-panel__actions">
64
- <button type="button" className="vg-button" disabled={!value} onClick={() => value && onCopy(value.text)}>
65
- {messages.copy}
66
- </button>
67
- </div>
68
- </div>
69
- );
70
- }
@@ -1,59 +0,0 @@
1
- import { useState } from "react";
2
- import type { ColumnSchema } from "vastgrid-core";
3
- import type { ColumnState } from "../controller.js";
4
- import type { Messages } from "../messages.js";
5
-
6
- /** Shows and hides columns. */
7
- export function ColumnsPanel({
8
- columns,
9
- schema,
10
- messages,
11
- onToggle,
12
- onShowAll,
13
- }: {
14
- columns: ColumnState[];
15
- schema: ColumnSchema[];
16
- messages: Messages;
17
- onToggle: (index: number, hidden: boolean) => void;
18
- onShowAll: () => void;
19
- }) {
20
- const [search, setSearch] = useState("");
21
- const needle = search.trim().toLowerCase();
22
- const visibleCount = columns.filter((column) => !column.hidden).length;
23
- const shown = columns.filter(
24
- (column) => !needle || (schema[column.index]?.name ?? "").toLowerCase().includes(needle),
25
- );
26
- return (
27
- <div className="vg-columns">
28
- <p className="vg-panel__title">{messages.columns}</p>
29
- <input
30
- type="search"
31
- className="vg-input"
32
- placeholder={messages.search}
33
- aria-label={messages.search}
34
- value={search}
35
- data-autofocus
36
- onChange={(event) => setSearch(event.target.value)}
37
- />
38
- <div className="vg-filter__values">
39
- {shown.map((column) => (
40
- <label className="vg-check" key={column.index}>
41
- <input
42
- type="checkbox"
43
- checked={!column.hidden}
44
- disabled={!column.hidden && visibleCount <= 1}
45
- onChange={(event) => onToggle(column.index, !event.target.checked)}
46
- />
47
- <span className="vg-check__label">{schema[column.index]?.name}</span>
48
- <span className="vg-check__count">{messages.types[schema[column.index]?.type ?? "unknown"]}</span>
49
- </label>
50
- ))}
51
- </div>
52
- <div className="vg-panel__actions">
53
- <button type="button" className="vg-button" onClick={onShowAll} disabled={visibleCount === columns.length}>
54
- {messages.showAllColumns}
55
- </button>
56
- </div>
57
- </div>
58
- );
59
- }
@@ -1,293 +0,0 @@
1
- import { type FormEvent, useEffect, useId, useMemo, useState } from "react";
2
- import {
3
- type CellValue,
4
- type ColumnSchema,
5
- type ColumnType,
6
- type DistinctValues,
7
- type Filter,
8
- isAbortError,
9
- isNumericType,
10
- isTemporalType,
11
- plainText,
12
- type ValueFormatter,
13
- } from "vastgrid-core";
14
- import type { Messages } from "../messages.js";
15
-
16
- type Operator = keyof Messages["operators"] | "none";
17
-
18
- const numericOperators: Operator[] = ["none", "eq", "ne", "gt", "ge", "lt", "le", "between", "blank", "notBlank"];
19
- const textOperators: Operator[] = [
20
- "none",
21
- "contains",
22
- "notContains",
23
- "startsWith",
24
- "endsWith",
25
- "equals",
26
- "notEquals",
27
- "blank",
28
- "notBlank",
29
- ];
30
-
31
- const NULL_KEY = "\u0000null";
32
-
33
- function keyOf(value: CellValue) {
34
- return value === null ? NULL_KEY : plainText(value);
35
- }
36
-
37
- function initialCondition(filter: Filter | null): { operator: Operator; first: string; second: string } {
38
- if (!filter) return { operator: "none", first: "", second: "" };
39
- switch (filter.type) {
40
- case "compare":
41
- return { operator: filter.op, first: plainText(filter.value), second: "" };
42
- case "between":
43
- return {
44
- operator: "between",
45
- first: filter.min === undefined ? "" : plainText(filter.min),
46
- second: filter.max === undefined ? "" : plainText(filter.max),
47
- };
48
- case "text":
49
- return { operator: filter.op, first: filter.value, second: "" };
50
- case "blank":
51
- return { operator: filter.blank ? "blank" : "notBlank", first: "", second: "" };
52
- default:
53
- return { operator: "none", first: "", second: "" };
54
- }
55
- }
56
-
57
- /**
58
- * Converts typed text for a numeric column into a number when it is one. Decimals and integers that a
59
- * JavaScript number cannot hold exactly (beyond 2^53) stay text, which servers compare exactly.
60
- */
61
- export function comparable(text: string, type: ColumnType): string | number {
62
- if (!isNumericType(type)) return text;
63
- const digits = text.trim().replaceAll(",", "");
64
- const number = Number(digits);
65
- if (digits === "" || !Number.isFinite(number)) return text;
66
- if (type === "decimal" || (/^[+-]?\d+$/.test(digits) && !Number.isSafeInteger(number))) return digits;
67
- return number;
68
- }
69
-
70
- /**
71
- * The filter for one column, in the manner of spreadsheet filters: a searchable list of values with
72
- * check boxes, or a condition (comparison, text match, range, blank).
73
- */
74
- export function FilterPanel({
75
- column,
76
- schema,
77
- filter,
78
- messages,
79
- formatter,
80
- count,
81
- load,
82
- onApply,
83
- }: {
84
- column: number;
85
- schema: ColumnSchema;
86
- filter: Filter | null;
87
- messages: Messages;
88
- formatter: ValueFormatter;
89
- count: (value: number) => string;
90
- load: (search: string, signal: AbortSignal) => Promise<DistinctValues> | null;
91
- onApply: (filter: Filter | null) => void;
92
- }) {
93
- const id = useId();
94
- const numeric = isNumericType(schema.type) || isTemporalType(schema.type);
95
- const operators = numeric
96
- ? numericOperators
97
- : schema.type === "boolean"
98
- ? (["none", "blank", "notBlank"] as Operator[])
99
- : textOperators;
100
- const [search, setSearch] = useState("");
101
- const [values, setValues] = useState<DistinctValues | null>(null);
102
- const [loading, setLoading] = useState(true);
103
- const [error, setError] = useState<string | null>(null);
104
- // Untouched values follow `base`; `toggled` holds the values the user changed.
105
- const [base, setBase] = useState(() => !(filter?.type === "values" && !filter.exclude));
106
- const [toggled, setToggled] = useState<Map<string, CellValue>>(() => {
107
- if (filter?.type !== "values") return new Map();
108
- return new Map(filter.values.map((value) => [keyOf(value), value]));
109
- });
110
- const [condition, setCondition] = useState(() => initialCondition(filter));
111
-
112
- useEffect(() => {
113
- const controller = new AbortController();
114
- const timer = setTimeout(
115
- () => {
116
- setLoading(true);
117
- const request = load(search, controller.signal);
118
- if (!request) {
119
- setLoading(false);
120
- return;
121
- }
122
- request
123
- .then((result) => {
124
- if (controller.signal.aborted) return;
125
- setValues(result);
126
- setError(null);
127
- setLoading(false);
128
- })
129
- .catch((cause: unknown) => {
130
- if (controller.signal.aborted || isAbortError(cause)) return;
131
- setError(cause instanceof Error ? cause.message : String(cause));
132
- setLoading(false);
133
- });
134
- },
135
- search ? 250 : 0,
136
- );
137
- return () => {
138
- clearTimeout(timer);
139
- controller.abort();
140
- };
141
- }, [load, search]);
142
-
143
- const isChecked = (value: CellValue) => (toggled.has(keyOf(value)) ? !base : base);
144
- const shown = values?.values ?? [];
145
- const allChecked = toggled.size === 0 ? base : shown.every((item) => isChecked(item.value)) && base;
146
-
147
- function toggle(value: CellValue) {
148
- setToggled((current) => {
149
- const next = new Map(current);
150
- const key = keyOf(value);
151
- if (next.has(key)) next.delete(key);
152
- else next.set(key, value);
153
- return next;
154
- });
155
- }
156
-
157
- function selectAll(checked: boolean) {
158
- setBase(checked);
159
- setToggled(new Map());
160
- }
161
-
162
- const valuesFilter = useMemo((): Filter | null => {
163
- const list = [...toggled.values()];
164
- if (base && list.length === 0) return null;
165
- if (base) return { type: "values", column, values: list, exclude: true };
166
- return { type: "values", column, values: list };
167
- }, [base, toggled, column]);
168
-
169
- function conditionFilter(): Filter | null {
170
- const { operator, first, second } = condition;
171
- switch (operator) {
172
- case "none":
173
- return null;
174
- case "blank":
175
- case "notBlank":
176
- return { type: "blank", column, blank: operator === "blank" };
177
- case "between":
178
- if (!first && !second) return null;
179
- return {
180
- type: "between",
181
- column,
182
- ...(first ? { min: comparable(first, schema.type) } : {}),
183
- ...(second ? { max: comparable(second, schema.type) } : {}),
184
- };
185
- case "eq":
186
- case "ne":
187
- case "lt":
188
- case "le":
189
- case "gt":
190
- case "ge":
191
- return first === "" ? null : { type: "compare", column, op: operator, value: comparable(first, schema.type) };
192
- default:
193
- return first === "" ? null : { type: "text", column, op: operator, value: first };
194
- }
195
- }
196
-
197
- function apply(event: FormEvent) {
198
- event.preventDefault();
199
- onApply(condition.operator === "none" ? valuesFilter : conditionFilter());
200
- }
201
-
202
- const needsValue =
203
- condition.operator !== "none" && condition.operator !== "blank" && condition.operator !== "notBlank";
204
-
205
- return (
206
- <form className="vg-filter" onSubmit={apply}>
207
- <p className="vg-panel__title" id={`${id}-title`}>
208
- {messages.filterBy(schema.name)}
209
- </p>
210
- <input
211
- type="search"
212
- className="vg-input"
213
- placeholder={messages.valuesSearch}
214
- aria-label={messages.valuesSearch}
215
- value={search}
216
- data-autofocus
217
- onChange={(event) => setSearch(event.target.value)}
218
- disabled={condition.operator !== "none"}
219
- />
220
- <fieldset className="vg-filter__values" aria-labelledby={`${id}-title`} aria-busy={loading}>
221
- {!search && (
222
- <label className="vg-check vg-check--all">
223
- <input
224
- type="checkbox"
225
- checked={allChecked}
226
- onChange={(event) => selectAll(event.target.checked)}
227
- disabled={condition.operator !== "none"}
228
- />
229
- <span>{messages.selectAll}</span>
230
- </label>
231
- )}
232
- {shown.map((item) => (
233
- <label className="vg-check" key={keyOf(item.value)}>
234
- <input
235
- type="checkbox"
236
- checked={isChecked(item.value)}
237
- onChange={() => toggle(item.value)}
238
- disabled={condition.operator !== "none"}
239
- />
240
- <span className="vg-check__label">
241
- {item.value === null || item.value === "" ? messages.blank : formatter.format(item.value, schema.type)}
242
- </span>
243
- <span className="vg-check__count">{count(item.count)}</span>
244
- </label>
245
- ))}
246
- {loading && shown.length === 0 && <p className="vg-panel__muted">{messages.loading}</p>}
247
- {error && <p className="vg-panel__error">{error}</p>}
248
- </fieldset>
249
- {values?.truncated && <p className="vg-panel__muted">{messages.valuesTruncated(count(shown.length))}</p>}
250
- <fieldset className="vg-filter__condition">
251
- <legend>{messages.condition}</legend>
252
- <select
253
- className="vg-input"
254
- aria-label={messages.condition}
255
- value={condition.operator}
256
- onChange={(event) => setCondition({ ...condition, operator: event.target.value as Operator })}
257
- >
258
- {operators.map((operator) => (
259
- <option key={operator} value={operator}>
260
- {operator === "none" ? messages.conditionNone : messages.operators[operator]}
261
- </option>
262
- ))}
263
- </select>
264
- {needsValue && (
265
- <input
266
- className="vg-input"
267
- aria-label={condition.operator === "between" ? messages.minimum : messages.value}
268
- placeholder={condition.operator === "between" ? messages.minimum : messages.value}
269
- value={condition.first}
270
- onChange={(event) => setCondition({ ...condition, first: event.target.value })}
271
- />
272
- )}
273
- {condition.operator === "between" && (
274
- <input
275
- className="vg-input"
276
- aria-label={messages.maximum}
277
- placeholder={messages.maximum}
278
- value={condition.second}
279
- onChange={(event) => setCondition({ ...condition, second: event.target.value })}
280
- />
281
- )}
282
- </fieldset>
283
- <div className="vg-panel__actions">
284
- <button type="button" className="vg-button" onClick={() => onApply(null)}>
285
- {messages.clear}
286
- </button>
287
- <button type="submit" className="vg-button vg-button--primary">
288
- {messages.apply}
289
- </button>
290
- </div>
291
- </form>
292
- );
293
- }