vastgrid 0.1.0 → 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 +43 -18
  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 -67
  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 -17
  258. package/dist/components/filter-panel.d.ts.map +0 -1
  259. package/dist/components/filter-panel.js +0 -165
  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 -218
  274. package/dist/controller.d.ts.map +0 -1
  275. package/dist/controller.js +0 -788
  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 -698
  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 -179
  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 -290
  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 -887
  308. package/src/data-grid.tsx +0 -1029
  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
@@ -0,0 +1,556 @@
1
+ import type { MeasureName } from "vastgrid-spec";
2
+ import type { FilterOperator } from "../core/filters.js";
3
+ import type { Statistic } from "../core/measures.js";
4
+
5
+ /** The texts of the grid's UI. Hosts replace any of them with the `messages` prop. */
6
+ export interface GridMessages {
7
+ grid: string;
8
+ rows: (count: string) => string;
9
+ loading: string;
10
+ reading: string;
11
+ failed: (message: string) => string;
12
+ cell: (row: string, column: string) => string;
13
+ count: (count: string) => string;
14
+ sum: (sum: string) => string;
15
+ average: (average: string) => string;
16
+ copied: (cells: string) => string;
17
+ search: string;
18
+ /** The cell editor's label. */
19
+ editing: (row: string, column: string) => string;
20
+ /** How the editor saves and leaves, for screen readers. */
21
+ editorHint: string;
22
+ readOnly: string;
23
+ invalidValue: (column: string) => string;
24
+ /** The button that saves the written cells into the file. */
25
+ save: string;
26
+ saving: string;
27
+ saved: (records: string) => string;
28
+ /** Written cells that the file does not hold yet. */
29
+ unsaved: (cells: string) => string;
30
+ /** The file changed on disk since it was read: the save was refused, and the written cells are kept. */
31
+ fileChanged: string;
32
+ saveUnsupported: string;
33
+ /** The file changed on disk while cells were written: they are kept, and the file is not read again. */
34
+ changedOnDisk: string;
35
+ reload: string;
36
+ /** Reading the file again drops the cells written into the table as it was read. */
37
+ discardAndReload: string;
38
+ /** A write that failed (§6.6): why, and the buttons that send it again, write over the cells or forget it. */
39
+ writeFailed: (message: string) => string;
40
+ writeConflict: string;
41
+ writeRevisionChanged: string;
42
+ retry: string;
43
+ overwrite: string;
44
+ discard: string;
45
+ /** The menu that exports the view (§6.6), its choices, and what an export did. */
46
+ export: string;
47
+ exportFormats: Record<string, string>;
48
+ exportView: string;
49
+ exportTable: string;
50
+ exportAllColumns: string;
51
+ exporting: string;
52
+ exported: (rows: string) => string;
53
+ /** The menu of derived columns written as formulas (§4.11), and its fields and buttons. */
54
+ formulas: string;
55
+ newFormula: string;
56
+ formulaName: string;
57
+ formula: string;
58
+ formulaExample: string;
59
+ addFormula: string;
60
+ applyFormula: string;
61
+ removeFormula: string;
62
+ /** The menu of a column (§6.2, §6.3): its label, sorting, filtering, pins, width and hiding. */
63
+ columnMenu: (column: string) => string;
64
+ sortAscending: string;
65
+ sortDescending: string;
66
+ thenSortAscending: string;
67
+ thenSortDescending: string;
68
+ clearSort: string;
69
+ filter: string;
70
+ filterBy: (column: string) => string;
71
+ clearFilter: string;
72
+ pinLeft: string;
73
+ pinRight: string;
74
+ unpin: string;
75
+ autoFit: string;
76
+ hideColumn: string;
77
+ showAllColumns: string;
78
+ /** The filter of a column: a list of its values with check boxes, or a condition. */
79
+ valuesSearch: string;
80
+ selectAll: string;
81
+ blank: string;
82
+ valuesTruncated: (shown: string) => string;
83
+ condition: string;
84
+ conditionNone: string;
85
+ operators: Record<FilterOperator, string>;
86
+ value: string;
87
+ minimum: string;
88
+ maximum: string;
89
+ /** The column has conditions that the menu does not show; applying the menu's filter replaces them. */
90
+ otherConditions: string;
91
+ back: string;
92
+ apply: string;
93
+ /** The statistics of a column, over the rows of the view. */
94
+ statistics: string;
95
+ statisticsOf: (column: string) => string;
96
+ statisticNames: Record<Statistic["kind"] | "nulls", string>;
97
+ /** The total of a column in the totals row (§5.1), and the names of the measures (§4.5). */
98
+ total: string;
99
+ noTotal: string;
100
+ measureNames: Record<MeasureName, string>;
101
+ /** Grouping rows (§6.2): from a column's menu, and the toolbar's menu of keys and measures. */
102
+ groupByColumn: string;
103
+ removeFromGroups: string;
104
+ ungroup: string;
105
+ groups: string;
106
+ groupKeys: string;
107
+ addKey: string;
108
+ measures: string;
109
+ addMeasure: string;
110
+ measureColumn: string;
111
+ allRows: string;
112
+ remove: (item: string) => string;
113
+ /** The pivot of the groups (§4.6): the columns whose values become columns. */
114
+ pivotBy: string;
115
+ addPivot: string;
116
+ /** The conditions menu (§6.2.1): groups of conditions across columns. */
117
+ conditions: string;
118
+ conditionsCount: (count: string) => string;
119
+ conditionsTitle: string;
120
+ conditionColumn: string;
121
+ matchLabel: string;
122
+ matchAll: string;
123
+ matchAny: string;
124
+ noConditions: string;
125
+ addCondition: string;
126
+ addGroup: string;
127
+ removeGroup: string;
128
+ removeCondition: string;
129
+ invalidCondition: string;
130
+ clearConditions: string;
131
+ /** The columns menu (§6.3): every column of the view, shown or hidden, in order. */
132
+ columns: string;
133
+ findColumn: string;
134
+ moveUp: (column: string) => string;
135
+ moveDown: (column: string) => string;
136
+ pinned: Record<"left" | "right", string>;
137
+ /** A column's format (§5.1): label, alignment, wrapping, number and date formats, and conditional colors. */
138
+ format: string;
139
+ formatOf: (column: string) => string;
140
+ columnLabel: string;
141
+ alignment: string;
142
+ alignments: Record<"auto" | "left" | "center" | "right", string>;
143
+ wrapText: string;
144
+ numberFormat: string;
145
+ numberStyles: Record<"none" | "decimal" | "percent" | "currency", string>;
146
+ currency: string;
147
+ invalidCurrency: string;
148
+ fractionDigits: string;
149
+ grouping: string;
150
+ datePattern: string;
151
+ conditionalColors: string;
152
+ textColor: string;
153
+ backgroundColor: string;
154
+ ruleDescription: string;
155
+ addRule: string;
156
+ removeRule: string;
157
+ clearFormat: string;
158
+ /** The detail panel of the active row (§5.2). */
159
+ details: string;
160
+ detailOf: (row: string) => string;
161
+ closeDetails: string;
162
+ /** Checked rows (§5.2.1): how many, or that the view may not match them. */
163
+ checkedRows: (count: string) => string;
164
+ checkedUnknown: string;
165
+ /** The find bar (⌘F) and the cell menu. */
166
+ find: string;
167
+ matchCase: string;
168
+ findNext: string;
169
+ findPrevious: string;
170
+ closeFind: string;
171
+ noMatches: string;
172
+ cellMenu: string;
173
+ copy: string;
174
+ filterToValue: string;
175
+ excludeValue: string;
176
+ fullValue: string;
177
+ close: string;
178
+ }
179
+
180
+ const en: GridMessages = {
181
+ grid: "Data table",
182
+ rows: (count) => `${count} rows`,
183
+ loading: "Loading…",
184
+ reading: "Reading the view…",
185
+ failed: (message) => `Failed: ${message}`,
186
+ cell: (row, column) => `Row ${row}, ${column}`,
187
+ count: (count) => `Count ${count}`,
188
+ sum: (sum) => `Sum ${sum}`,
189
+ average: (average) => `Average ${average}`,
190
+ copied: (cells) => `Copied ${cells} cells`,
191
+ search: "Search",
192
+ editing: (row, column) => `Editing row ${row}, ${column}`,
193
+ editorHint: "Enter saves, Esc cancels, Alt+Enter starts a new line",
194
+ readOnly: "This cell is not written.",
195
+ invalidValue: (column) => `This is not a value of ${column}.`,
196
+ save: "Save",
197
+ saving: "Saving…",
198
+ saved: (records) => `Saved rows: ${records}`,
199
+ unsaved: (cells) => `Unsaved ${cells}`,
200
+ fileChanged:
201
+ "Not saved: the file was changed by someone else. Your edits are kept; open the file again to see the changes.",
202
+ saveUnsupported: "This file cannot be saved.",
203
+ changedOnDisk: "The file changed on disk. Your unsaved edits are kept.",
204
+ reload: "Reload",
205
+ discardAndReload: "Discard edits and reload",
206
+ writeFailed: (message) => `Not written: ${message}`,
207
+ writeConflict: "Not written: the cells were changed meanwhile. Their values now are shown.",
208
+ writeRevisionChanged: "Not written: the table was read again, so its rows may be other records now.",
209
+ retry: "Retry",
210
+ overwrite: "Overwrite",
211
+ discard: "Discard",
212
+ export: "Export",
213
+ exportFormats: { csv: "CSV", tsv: "TSV", xlsx: "Excel (.xlsx)", parquet: "Parquet", json: "JSON" },
214
+ exportView: "Rows of the view",
215
+ exportTable: "Whole table",
216
+ exportAllColumns: "Hidden columns too",
217
+ exporting: "Exporting…",
218
+ exported: (rows) => `Exported ${rows} rows`,
219
+ formulas: "Formulas",
220
+ newFormula: "New column",
221
+ formulaName: "Column name",
222
+ formula: "Formula",
223
+ formulaExample: "qty * price",
224
+ addFormula: "Add",
225
+ applyFormula: "Apply",
226
+ removeFormula: "Remove",
227
+ columnMenu: (column) => `${column} column menu`,
228
+ sortAscending: "Sort ascending",
229
+ sortDescending: "Sort descending",
230
+ thenSortAscending: "Then sort ascending",
231
+ thenSortDescending: "Then sort descending",
232
+ clearSort: "Clear sort",
233
+ filter: "Filter…",
234
+ filterBy: (column) => `Filter ${column}`,
235
+ clearFilter: "Clear filter",
236
+ pinLeft: "Freeze at the left",
237
+ pinRight: "Freeze at the right",
238
+ unpin: "Unfreeze",
239
+ autoFit: "Fit width to content",
240
+ hideColumn: "Hide column",
241
+ showAllColumns: "Show all columns",
242
+ valuesSearch: "Search values",
243
+ selectAll: "(Select all)",
244
+ blank: "(Blank)",
245
+ valuesTruncated: (shown) => `Showing the ${shown} most common values. Search to find others.`,
246
+ condition: "Condition",
247
+ conditionNone: "None",
248
+ operators: {
249
+ eq: "Equals",
250
+ ne: "Does not equal",
251
+ lt: "Less than",
252
+ le: "Less than or equal to",
253
+ gt: "Greater than",
254
+ ge: "Greater than or equal to",
255
+ between: "Between",
256
+ contains: "Contains",
257
+ notContains: "Does not contain",
258
+ startsWith: "Begins with",
259
+ endsWith: "Ends with",
260
+ blank: "Is blank",
261
+ notBlank: "Is not blank",
262
+ },
263
+ value: "Value",
264
+ minimum: "From",
265
+ maximum: "To",
266
+ otherConditions: "This column has other conditions. Applying replaces them.",
267
+ back: "Back",
268
+ apply: "Apply",
269
+ statistics: "Statistics…",
270
+ statisticsOf: (column) => `Statistics of ${column}`,
271
+ statisticNames: {
272
+ rows: "Rows",
273
+ values: "Values",
274
+ nulls: "Empty",
275
+ distinct: "Distinct",
276
+ min: "Minimum",
277
+ max: "Maximum",
278
+ sum: "Sum",
279
+ average: "Average",
280
+ median: "Median",
281
+ stddev: "Standard deviation",
282
+ },
283
+ total: "Total",
284
+ noTotal: "No total",
285
+ measureNames: {
286
+ count: "Count",
287
+ count_distinct: "Distinct count",
288
+ sum: "Sum",
289
+ avg: "Average",
290
+ stddev: "Standard deviation",
291
+ min: "Minimum",
292
+ max: "Maximum",
293
+ quantile: "Quantile",
294
+ },
295
+ groupByColumn: "Group by this column",
296
+ removeFromGroups: "Stop grouping by this column",
297
+ ungroup: "Ungroup",
298
+ groups: "Group",
299
+ groupKeys: "Group by",
300
+ addKey: "Add a column…",
301
+ measures: "Measures",
302
+ addMeasure: "Add",
303
+ measureColumn: "Column",
304
+ allRows: "(rows)",
305
+ remove: (item) => `Remove ${item}`,
306
+ pivotBy: "Columns from values of",
307
+ addPivot: "Add a column…",
308
+ conditions: "Conditions",
309
+ conditionsCount: (count) => `Conditions (${count})`,
310
+ conditionsTitle: "Filter conditions",
311
+ conditionColumn: "Column",
312
+ matchLabel: "Match",
313
+ matchAll: "All of these (AND)",
314
+ matchAny: "Any of these (OR)",
315
+ noConditions: "No conditions. Add one to filter the rows.",
316
+ addCondition: "Add condition",
317
+ addGroup: "Add group",
318
+ removeGroup: "Remove group",
319
+ removeCondition: "Remove condition",
320
+ invalidCondition: "A value is not a value of its column.",
321
+ clearConditions: "Clear all",
322
+ columns: "Columns",
323
+ findColumn: "Find a column",
324
+ moveUp: (column) => `Move ${column} left`,
325
+ moveDown: (column) => `Move ${column} right`,
326
+ pinned: { left: "Frozen left", right: "Frozen right" },
327
+ format: "Format…",
328
+ formatOf: (column) => `Format of ${column}`,
329
+ columnLabel: "Label",
330
+ alignment: "Alignment",
331
+ alignments: { auto: "By type", left: "Left", center: "Center", right: "Right" },
332
+ wrapText: "Wrap text",
333
+ numberFormat: "Numbers",
334
+ numberStyles: { none: "As they are", decimal: "Number", percent: "Percent", currency: "Currency" },
335
+ currency: "Currency code",
336
+ invalidCurrency: "This is not a currency code (such as JPY or USD).",
337
+ fractionDigits: "Decimal places",
338
+ grouping: "Group digits",
339
+ datePattern: "Date pattern",
340
+ conditionalColors: "Conditional colors",
341
+ textColor: "Text",
342
+ backgroundColor: "Background",
343
+ ruleDescription: "What the colors mean",
344
+ addRule: "Add rule",
345
+ removeRule: "Remove rule",
346
+ clearFormat: "Clear format",
347
+ details: "Details",
348
+ detailOf: (row) => `Row ${row}`,
349
+ closeDetails: "Close details",
350
+ checkedRows: (count) => `${count} checked`,
351
+ checkedUnknown: "Some rows of this view may not be checked: filter as when they were checked, or clear the checks.",
352
+ find: "Find",
353
+ matchCase: "Match case",
354
+ findNext: "Find next",
355
+ findPrevious: "Find previous",
356
+ closeFind: "Close find",
357
+ noMatches: "No matches",
358
+ cellMenu: "Cell menu",
359
+ copy: "Copy",
360
+ filterToValue: "Filter to this value",
361
+ excludeValue: "Exclude this value",
362
+ fullValue: "Show full value",
363
+ close: "Close",
364
+ };
365
+
366
+ const ja: GridMessages = {
367
+ grid: "データの表",
368
+ rows: (count) => `${count} 行`,
369
+ loading: "読み込み中…",
370
+ reading: "ビューを読んでいます…",
371
+ failed: (message) => `失敗しました: ${message}`,
372
+ cell: (row, column) => `${row} 行目・${column}`,
373
+ count: (count) => `個数 ${count}`,
374
+ sum: (sum) => `合計 ${sum}`,
375
+ average: (average) => `平均 ${average}`,
376
+ copied: (cells) => `${cells} セルをコピーしました`,
377
+ search: "検索",
378
+ editing: (row, column) => `${row} 行目・${column} を編集中`,
379
+ editorHint: "Enter で保存、Esc で取り消し、Alt+Enter で改行",
380
+ readOnly: "このセルは書き込めません。",
381
+ invalidValue: (column) => `${column} の値として読めません。`,
382
+ save: "保存",
383
+ saving: "保存しています…",
384
+ saved: (records) => `${records} 行を保存しました`,
385
+ unsaved: (cells) => `未保存 ${cells}`,
386
+ fileChanged:
387
+ "保存しませんでした: ファイルがほかで変更されています。編集は残っています。変更を見るにはファイルを開き直してください。",
388
+ saveUnsupported: "このファイルは保存できません。",
389
+ changedOnDisk: "ディスク上のファイルが変更されました。未保存の編集は残っています。",
390
+ reload: "再読み込み",
391
+ discardAndReload: "編集を破棄して再読み込み",
392
+ writeFailed: (message) => `書き込めませんでした: ${message}`,
393
+ writeConflict: "書き込めませんでした: その間にセルが変更されています。いまの値を表示しています。",
394
+ writeRevisionChanged: "書き込めませんでした: 表を読み直したため、行がほかの記録になっているかもしれません。",
395
+ retry: "再試行",
396
+ overwrite: "上書き",
397
+ discard: "破棄",
398
+ export: "書き出し",
399
+ exportFormats: { csv: "CSV", tsv: "TSV", xlsx: "Excel (.xlsx)", parquet: "Parquet", json: "JSON" },
400
+ exportView: "ビューの行",
401
+ exportTable: "表全体",
402
+ exportAllColumns: "非表示の列も含める",
403
+ exporting: "書き出しています…",
404
+ exported: (rows) => `${rows} 行を書き出しました`,
405
+ formulas: "計算列",
406
+ newFormula: "新しい列",
407
+ formulaName: "列名",
408
+ formula: "式",
409
+ formulaExample: "qty * price",
410
+ addFormula: "追加",
411
+ applyFormula: "適用",
412
+ removeFormula: "削除",
413
+ columnMenu: (column) => `${column} 列のメニュー`,
414
+ sortAscending: "昇順に並べ替え",
415
+ sortDescending: "降順に並べ替え",
416
+ thenSortAscending: "次の順で昇順に並べ替え",
417
+ thenSortDescending: "次の順で降順に並べ替え",
418
+ clearSort: "並べ替えを解除",
419
+ filter: "絞り込み…",
420
+ filterBy: (column) => `${column} で絞り込み`,
421
+ clearFilter: "絞り込みを解除",
422
+ pinLeft: "左端に固定",
423
+ pinRight: "右端に固定",
424
+ unpin: "固定を解除",
425
+ autoFit: "幅を内容に合わせる",
426
+ hideColumn: "列を隠す",
427
+ showAllColumns: "すべての列を表示",
428
+ valuesSearch: "値を検索",
429
+ selectAll: "(すべて選択)",
430
+ blank: "(空白)",
431
+ valuesTruncated: (shown) => `多い順に ${shown} 件を表示しています。ほかの値は検索してください。`,
432
+ condition: "条件",
433
+ conditionNone: "なし",
434
+ operators: {
435
+ eq: "等しい",
436
+ ne: "等しくない",
437
+ lt: "より小さい",
438
+ le: "以下",
439
+ gt: "より大きい",
440
+ ge: "以上",
441
+ between: "範囲",
442
+ contains: "を含む",
443
+ notContains: "を含まない",
444
+ startsWith: "で始まる",
445
+ endsWith: "で終わる",
446
+ blank: "空白",
447
+ notBlank: "空白以外",
448
+ },
449
+ value: "値",
450
+ minimum: "下限",
451
+ maximum: "上限",
452
+ otherConditions: "この列にはほかの条件があります。適用すると置き換えます。",
453
+ back: "戻る",
454
+ apply: "適用",
455
+ statistics: "統計…",
456
+ statisticsOf: (column) => `${column} の統計`,
457
+ statisticNames: {
458
+ rows: "行数",
459
+ values: "値の数",
460
+ nulls: "空",
461
+ distinct: "異なる値の数",
462
+ min: "最小",
463
+ max: "最大",
464
+ sum: "合計",
465
+ average: "平均",
466
+ median: "中央値",
467
+ stddev: "標準偏差",
468
+ },
469
+ total: "集計行",
470
+ noTotal: "集計しない",
471
+ measureNames: {
472
+ count: "個数",
473
+ count_distinct: "異なる値の数",
474
+ sum: "合計",
475
+ avg: "平均",
476
+ stddev: "標準偏差",
477
+ min: "最小",
478
+ max: "最大",
479
+ quantile: "分位数",
480
+ },
481
+ groupByColumn: "この列でグループ化",
482
+ removeFromGroups: "この列でのグループ化をやめる",
483
+ ungroup: "グループ化を解除",
484
+ groups: "グループ",
485
+ groupKeys: "グループ化する列",
486
+ addKey: "列を追加…",
487
+ measures: "集計",
488
+ addMeasure: "追加",
489
+ measureColumn: "列",
490
+ allRows: "(行)",
491
+ remove: (item) => `${item} を削除`,
492
+ pivotBy: "値を列にする列",
493
+ addPivot: "列を追加…",
494
+ conditions: "条件",
495
+ conditionsCount: (count) => `条件(${count})`,
496
+ conditionsTitle: "絞り込み条件",
497
+ conditionColumn: "列",
498
+ matchLabel: "一致",
499
+ matchAll: "すべてを満たす(AND)",
500
+ matchAny: "いずれかを満たす(OR)",
501
+ noConditions: "条件はありません。条件を追加すると行を絞り込みます。",
502
+ addCondition: "条件を追加",
503
+ addGroup: "グループを追加",
504
+ removeGroup: "グループを削除",
505
+ removeCondition: "条件を削除",
506
+ invalidCondition: "列の値として読めない値があります。",
507
+ clearConditions: "すべて解除",
508
+ columns: "列",
509
+ findColumn: "列を検索",
510
+ moveUp: (column) => `${column} を左へ移動`,
511
+ moveDown: (column) => `${column} を右へ移動`,
512
+ pinned: { left: "左に固定", right: "右に固定" },
513
+ format: "書式…",
514
+ formatOf: (column) => `${column} の書式`,
515
+ columnLabel: "表示名",
516
+ alignment: "配置",
517
+ alignments: { auto: "型に合わせる", left: "左", center: "中央", right: "右" },
518
+ wrapText: "折り返して表示",
519
+ numberFormat: "数値",
520
+ numberStyles: { none: "そのまま", decimal: "数値", percent: "パーセント", currency: "通貨" },
521
+ currency: "通貨コード",
522
+ invalidCurrency: "通貨コード(JPY・USD など)ではありません。",
523
+ fractionDigits: "小数点以下の桁数",
524
+ grouping: "桁区切り",
525
+ datePattern: "日付の形式",
526
+ conditionalColors: "条件付きの色",
527
+ textColor: "文字",
528
+ backgroundColor: "背景",
529
+ ruleDescription: "色の意味",
530
+ addRule: "規則を追加",
531
+ removeRule: "規則を削除",
532
+ clearFormat: "書式を解除",
533
+ details: "詳細",
534
+ detailOf: (row) => `${row} 行目`,
535
+ closeDetails: "詳細を閉じる",
536
+ checkedRows: (count) => `${count} 行をチェック`,
537
+ checkedUnknown:
538
+ "このビューにはチェックの状態が分からない行があります。チェックしたときの絞り込みに戻すか、チェックを解除してください。",
539
+ find: "検索",
540
+ matchCase: "大文字と小文字を区別",
541
+ findNext: "次を検索",
542
+ findPrevious: "前を検索",
543
+ closeFind: "検索を閉じる",
544
+ noMatches: "見つかりません",
545
+ cellMenu: "セルのメニュー",
546
+ copy: "コピー",
547
+ filterToValue: "この値で絞り込む",
548
+ excludeValue: "この値を除く",
549
+ fullValue: "値の全体を表示",
550
+ close: "閉じる",
551
+ };
552
+
553
+ /** The built-in texts for a language tag: Japanese for `ja`, English otherwise. */
554
+ export function messagesFor(locale: string | undefined): GridMessages {
555
+ return locale?.toLowerCase().startsWith("ja") ? ja : en;
556
+ }
@@ -0,0 +1,42 @@
1
+ import { useRef, useState, useTransition } from "react";
2
+ import { CommandError } from "vastgrid-spec";
3
+ import type { GridStore } from "../core/store.js";
4
+ import type { GridLocale, GridSave } from "./context.js";
5
+
6
+ /**
7
+ * Saves the written cells into the engine's file (the `save` command, §7.5 of the specification) as an Action:
8
+ * `useTransition` gives its pending state, and its outcome (the records saved, a file changed by someone else, or
9
+ * why it failed) is shown and announced.
10
+ */
11
+ export function useSaveAction(store: GridStore, language: GridLocale, announce: (text: string) => void): GridSave {
12
+ const [pending, startTransition] = useTransition();
13
+ const [outcome, setOutcome] = useState<GridSave["outcome"]>(null);
14
+ const running = useRef<Promise<unknown>>(Promise.resolve());
15
+ const save = () => {
16
+ startTransition(async () => {
17
+ const previous = running.current;
18
+ const run = previous.then(() => store.dispatch({ command: "save" }));
19
+ running.current = run;
20
+ const result = await run;
21
+ const { messages, number } = language;
22
+ const table = store.getSnapshot().table;
23
+ const revision = table?.revision ?? null;
24
+ const version = table?.edits.version ?? 0;
25
+ let next: NonNullable<GridSave["outcome"]>;
26
+ if (result.ok) {
27
+ const saved = result.output as { records: number; edits: { version: number } };
28
+ const text = messages.saved(number.format(saved.records));
29
+ next = { text, failed: false, revision, version: saved.edits.version };
30
+ } else if (result.error instanceof CommandError && result.error.reason === "unsupported") {
31
+ next = { text: messages.saveUnsupported, failed: true, revision, version };
32
+ } else if (result.error.code === "file_changed") {
33
+ next = { text: messages.fileChanged, failed: true, revision, version };
34
+ } else {
35
+ next = { text: messages.failed(result.error.message), failed: true, revision, version };
36
+ }
37
+ startTransition(() => setOutcome(next));
38
+ announce(next.text);
39
+ });
40
+ };
41
+ return { save, pending, outcome };
42
+ }
@@ -0,0 +1,59 @@
1
+ import { type Column, textOf } from "vastgrid-spec";
2
+ import { readRows, useGridSelector, useGridStore } from "./hooks.js";
3
+
4
+ /** Rows that the server sends as HTML at most. */
5
+ const MAX_ROWS = 100;
6
+
7
+ function alignOf(column: Column) {
8
+ return column.type === "integer" || column.type === "bigint" || column.type === "decimal" || column.type === "float"
9
+ ? "vg-static__number"
10
+ : undefined;
11
+ }
12
+
13
+ /**
14
+ * What the server sends in place of the canvas (§7 of react.md): the first rows that the store has, from
15
+ * `initial`, as an HTML table that is readable before any script runs; without rows, an empty body that is
16
+ * busy. It has the size of the body, so that the canvas replaces it without moving anything.
17
+ */
18
+ export function StaticRows({ label }: { label: string }) {
19
+ const store = useGridStore();
20
+ const view = useGridSelector((snapshot) => snapshot.view);
21
+ const columns = useGridSelector((snapshot) => snapshot.displayed);
22
+ if (!view) return <div className="vg-body" aria-busy="true" />;
23
+ const names = columns.map((item) => item.column.name);
24
+ // The rows that the store has from the start of the view, which `initial` gave.
25
+ const read = readRows(store, 0, 0, Math.min(view.rowCount, MAX_ROWS), names);
26
+ const loaded = read.findIndex((values) => values.every((value) => value === undefined));
27
+ const rows = loaded < 0 ? read : read.slice(0, loaded);
28
+ return (
29
+ <div className="vg-body" aria-busy="true">
30
+ <table className="vg-static" aria-label={label} aria-rowcount={view.rowCount + 1}>
31
+ <thead>
32
+ <tr>
33
+ {columns.map((item) => (
34
+ <th key={item.column.name} scope="col" className={alignOf(item.column)}>
35
+ {item.setting.format?.label ?? item.column.name}
36
+ </th>
37
+ ))}
38
+ </tr>
39
+ </thead>
40
+ <tbody>
41
+ {rows.map((values, row) => (
42
+ // Rows of a view have no other identity here: the server renders them once.
43
+ // biome-ignore lint/suspicious/noArrayIndexKey: the rows are the view's first rows, in order.
44
+ <tr key={row}>
45
+ {values.map((value, index) => {
46
+ const column = columns[index].column;
47
+ return (
48
+ <td key={column.name} className={alignOf(column)}>
49
+ {value === undefined || value === null ? "" : (textOf(value, column) ?? "")}
50
+ </td>
51
+ );
52
+ })}
53
+ </tr>
54
+ ))}
55
+ </tbody>
56
+ </table>
57
+ </div>
58
+ );
59
+ }