@jcsoftdev/ui 0.0.0-stage → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (1626) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +132 -2
  3. package/bin/ui-theme.mjs +45 -0
  4. package/dist/ai/attachment/attachment.d.ts +19 -0
  5. package/dist/ai/attachment/attachment.js +87 -0
  6. package/dist/ai/attachment/index.d.ts +1 -0
  7. package/dist/ai/attachment/index.js +1 -0
  8. package/dist/ai/chain-of-thought/chain-of-thought.d.ts +21 -0
  9. package/dist/ai/chain-of-thought/chain-of-thought.js +88 -0
  10. package/dist/ai/chain-of-thought/index.d.ts +1 -0
  11. package/dist/ai/chain-of-thought/index.js +1 -0
  12. package/dist/ai/chat-list/chat-list.d.ts +24 -0
  13. package/dist/ai/chat-list/chat-list.js +94 -0
  14. package/dist/ai/chat-list/group.d.ts +12 -0
  15. package/dist/ai/chat-list/group.js +37 -0
  16. package/dist/ai/chat-list/index.d.ts +2 -0
  17. package/dist/ai/chat-list/index.js +7 -0
  18. package/dist/ai/chat-message/chat-message.d.ts +23 -0
  19. package/dist/ai/chat-message/chat-message.js +91 -0
  20. package/dist/ai/chat-message/index.d.ts +1 -0
  21. package/dist/ai/chat-message/index.js +1 -0
  22. package/dist/ai/code-block/code-block.d.ts +12 -0
  23. package/dist/ai/code-block/code-block.js +80 -0
  24. package/dist/ai/code-block/highlight.d.ts +9 -0
  25. package/dist/ai/code-block/highlight.js +160 -0
  26. package/dist/ai/code-block/index.d.ts +2 -0
  27. package/dist/ai/code-block/index.js +9 -0
  28. package/dist/ai/conversation/conversation.d.ts +12 -0
  29. package/dist/ai/conversation/conversation.js +87 -0
  30. package/dist/ai/conversation/index.d.ts +2 -0
  31. package/dist/ai/conversation/index.js +15 -0
  32. package/dist/ai/conversation/stick.d.ts +18 -0
  33. package/dist/ai/conversation/stick.js +22 -0
  34. package/dist/ai/index.d.ts +14 -0
  35. package/dist/ai/index.js +14 -0
  36. package/dist/ai/internal/disclosure.d.ts +13 -0
  37. package/dist/ai/internal/disclosure.js +62 -0
  38. package/dist/ai/internal/icon-action.d.ts +7 -0
  39. package/dist/ai/internal/icon-action.js +21 -0
  40. package/dist/ai/internal/keyframes.d.ts +5 -0
  41. package/dist/ai/internal/keyframes.js +7 -0
  42. package/dist/ai/internal/use-copy.d.ts +5 -0
  43. package/dist/ai/internal/use-copy.js +35 -0
  44. package/dist/ai/loader/index.d.ts +1 -0
  45. package/dist/ai/loader/index.js +1 -0
  46. package/dist/ai/loader/loader.d.ts +12 -0
  47. package/dist/ai/loader/loader.js +41 -0
  48. package/dist/ai/markdown/index.d.ts +2 -0
  49. package/dist/ai/markdown/index.js +6 -0
  50. package/dist/ai/markdown/markdown.d.ts +12 -0
  51. package/dist/ai/markdown/markdown.js +159 -0
  52. package/dist/ai/markdown/parse.d.ts +56 -0
  53. package/dist/ai/markdown/parse.js +229 -0
  54. package/dist/ai/message-actions/index.d.ts +1 -0
  55. package/dist/ai/message-actions/index.js +1 -0
  56. package/dist/ai/message-actions/message-actions.d.ts +15 -0
  57. package/dist/ai/message-actions/message-actions.js +72 -0
  58. package/dist/ai/prompt-input/index.d.ts +2 -0
  59. package/dist/ai/prompt-input/index.js +5 -0
  60. package/dist/ai/prompt-input/keys.d.ts +8 -0
  61. package/dist/ai/prompt-input/keys.js +6 -0
  62. package/dist/ai/prompt-input/prompt-input.d.ts +29 -0
  63. package/dist/ai/prompt-input/prompt-input.js +110 -0
  64. package/dist/ai/prompt-suggestion/index.d.ts +1 -0
  65. package/dist/ai/prompt-suggestion/index.js +1 -0
  66. package/dist/ai/prompt-suggestion/prompt-suggestion.d.ts +13 -0
  67. package/dist/ai/prompt-suggestion/prompt-suggestion.js +44 -0
  68. package/dist/ai/source/index.d.ts +1 -0
  69. package/dist/ai/source/index.js +1 -0
  70. package/dist/ai/source/source.d.ts +12 -0
  71. package/dist/ai/source/source.js +48 -0
  72. package/dist/ai/text-shimmer/index.d.ts +1 -0
  73. package/dist/ai/text-shimmer/index.js +1 -0
  74. package/dist/ai/text-shimmer/text-shimmer.d.ts +11 -0
  75. package/dist/ai/text-shimmer/text-shimmer.js +28 -0
  76. package/dist/ai/tool-call/index.d.ts +1 -0
  77. package/dist/ai/tool-call/index.js +1 -0
  78. package/dist/ai/tool-call/tool-call.d.ts +18 -0
  79. package/dist/ai/tool-call/tool-call.js +84 -0
  80. package/dist/atoms/avatar/avatar.d.ts +11 -0
  81. package/dist/atoms/avatar/avatar.js +39 -0
  82. package/dist/atoms/avatar/index.d.ts +1 -0
  83. package/dist/atoms/avatar/index.js +4 -0
  84. package/dist/atoms/avatar-group/avatar-group.d.ts +7 -0
  85. package/dist/atoms/avatar-group/avatar-group.js +8 -0
  86. package/dist/atoms/avatar-group/index.d.ts +1 -0
  87. package/dist/atoms/avatar-group/index.js +4 -0
  88. package/dist/atoms/big-date/big-date.d.ts +7 -0
  89. package/dist/atoms/big-date/big-date.js +18 -0
  90. package/dist/atoms/big-date/index.d.ts +1 -0
  91. package/dist/atoms/big-date/index.js +4 -0
  92. package/dist/atoms/button/button.d.ts +11 -0
  93. package/dist/atoms/button/button.js +60 -0
  94. package/dist/atoms/button/index.d.ts +1 -0
  95. package/dist/atoms/button/index.js +6 -0
  96. package/dist/atoms/checkbox/checkbox.d.ts +5 -0
  97. package/dist/atoms/checkbox/checkbox.js +18 -0
  98. package/dist/atoms/checkbox/index.d.ts +1 -0
  99. package/dist/atoms/checkbox/index.js +4 -0
  100. package/dist/atoms/chip/chip.d.ts +8 -0
  101. package/dist/atoms/chip/chip.js +28 -0
  102. package/dist/atoms/chip/index.d.ts +1 -0
  103. package/dist/atoms/chip/index.js +4 -0
  104. package/dist/atoms/icon/icon-map.d.ts +42 -0
  105. package/dist/atoms/icon/icon-map.js +81 -0
  106. package/dist/atoms/icon/icon.d.ts +12 -0
  107. package/dist/atoms/icon/icon.js +13 -0
  108. package/dist/atoms/icon/index.d.ts +2 -0
  109. package/dist/atoms/icon/index.js +6 -0
  110. package/dist/atoms/index.d.ts +16 -0
  111. package/dist/atoms/index.js +16 -0
  112. package/dist/atoms/led/index.d.ts +1 -0
  113. package/dist/atoms/led/index.js +4 -0
  114. package/dist/atoms/led/led.d.ts +9 -0
  115. package/dist/atoms/led/led.js +23 -0
  116. package/dist/atoms/number-value/format.d.ts +10 -0
  117. package/dist/atoms/number-value/format.js +19 -0
  118. package/dist/atoms/number-value/index.d.ts +2 -0
  119. package/dist/atoms/number-value/index.js +14 -0
  120. package/dist/atoms/number-value/number-value.d.ts +18 -0
  121. package/dist/atoms/number-value/number-value.js +42 -0
  122. package/dist/atoms/pressable-feedback/index.d.ts +1 -0
  123. package/dist/atoms/pressable-feedback/index.js +4 -0
  124. package/dist/atoms/pressable-feedback/pressable-feedback.d.ts +14 -0
  125. package/dist/atoms/pressable-feedback/pressable-feedback.js +66 -0
  126. package/dist/atoms/progress/index.d.ts +1 -0
  127. package/dist/atoms/progress/index.js +5 -0
  128. package/dist/atoms/progress/progress.d.ts +9 -0
  129. package/dist/atoms/progress/progress.js +29 -0
  130. package/dist/atoms/radio-group/index.d.ts +1 -0
  131. package/dist/atoms/radio-group/index.js +6 -0
  132. package/dist/atoms/radio-group/radio-group.d.ts +11 -0
  133. package/dist/atoms/radio-group/radio-group.js +47 -0
  134. package/dist/atoms/status-dot/index.d.ts +1 -0
  135. package/dist/atoms/status-dot/index.js +6 -0
  136. package/dist/atoms/status-dot/status-dot.d.ts +13 -0
  137. package/dist/atoms/status-dot/status-dot.js +39 -0
  138. package/dist/atoms/status-pill/index.d.ts +1 -0
  139. package/dist/atoms/status-pill/index.js +4 -0
  140. package/dist/atoms/status-pill/status-pill.d.ts +10 -0
  141. package/dist/atoms/status-pill/status-pill.js +25 -0
  142. package/dist/atoms/switch/index.d.ts +1 -0
  143. package/dist/atoms/switch/index.js +4 -0
  144. package/dist/atoms/switch/switch.d.ts +5 -0
  145. package/dist/atoms/switch/switch.js +17 -0
  146. package/dist/atoms/trend-chip/index.d.ts +2 -0
  147. package/dist/atoms/trend-chip/index.js +12 -0
  148. package/dist/atoms/trend-chip/trend-chip.d.ts +14 -0
  149. package/dist/atoms/trend-chip/trend-chip.js +58 -0
  150. package/dist/atoms/trend-chip/trend.d.ts +8 -0
  151. package/dist/atoms/trend-chip/trend.js +21 -0
  152. package/dist/brand/export.d.ts +7 -0
  153. package/dist/brand/export.js +62 -0
  154. package/dist/brand/generate.d.ts +65 -0
  155. package/dist/brand/generate.js +409 -0
  156. package/dist/brand/glyphs.d.ts +16 -0
  157. package/dist/brand/glyphs.js +91 -0
  158. package/dist/brand/index.d.ts +8 -0
  159. package/dist/brand/index.js +8 -0
  160. package/dist/brand/initials.d.ts +6 -0
  161. package/dist/brand/initials.js +41 -0
  162. package/dist/brand/logo-forge.d.ts +42 -0
  163. package/dist/brand/logo-forge.js +441 -0
  164. package/dist/brand/logo-mark.d.ts +8 -0
  165. package/dist/brand/logo-mark.js +34 -0
  166. package/dist/brand/palettes.d.ts +23 -0
  167. package/dist/brand/palettes.js +47 -0
  168. package/dist/brand/rng.d.ts +5 -0
  169. package/dist/brand/rng.js +26 -0
  170. package/dist/calendar/day-chip/day-chip.d.ts +8 -0
  171. package/dist/calendar/day-chip/day-chip.js +34 -0
  172. package/dist/calendar/day-chip/index.d.ts +2 -0
  173. package/dist/calendar/day-chip/index.js +4 -0
  174. package/dist/calendar/event-block/event-block.d.ts +9 -0
  175. package/dist/calendar/event-block/event-block.js +74 -0
  176. package/dist/calendar/event-block/index.d.ts +2 -0
  177. package/dist/calendar/event-block/index.js +4 -0
  178. package/dist/calendar/index.d.ts +5 -0
  179. package/dist/calendar/index.js +5 -0
  180. package/dist/calendar/internal/date-utils.d.ts +18 -0
  181. package/dist/calendar/internal/date-utils.js +66 -0
  182. package/dist/calendar/internal/index.d.ts +3 -0
  183. package/dist/calendar/internal/index.js +3 -0
  184. package/dist/calendar/internal/layout.d.ts +14 -0
  185. package/dist/calendar/internal/layout.js +42 -0
  186. package/dist/calendar/internal/styles.d.ts +2 -0
  187. package/dist/calendar/internal/styles.js +4 -0
  188. package/dist/calendar/internal/types.d.ts +29 -0
  189. package/dist/calendar/internal/types.js +0 -0
  190. package/dist/calendar/month-view/index.d.ts +1 -0
  191. package/dist/calendar/month-view/index.js +4 -0
  192. package/dist/calendar/month-view/month-view.d.ts +24 -0
  193. package/dist/calendar/month-view/month-view.js +110 -0
  194. package/dist/calendar/timeline-view/index.d.ts +1 -0
  195. package/dist/calendar/timeline-view/index.js +4 -0
  196. package/dist/calendar/timeline-view/timeline-view.d.ts +19 -0
  197. package/dist/calendar/timeline-view/timeline-view.js +64 -0
  198. package/dist/calendar/week-view/index.d.ts +1 -0
  199. package/dist/calendar/week-view/index.js +4 -0
  200. package/dist/calendar/week-view/week-view.d.ts +26 -0
  201. package/dist/calendar/week-view/week-view.js +172 -0
  202. package/dist/charts/area-chart/area-chart.d.ts +10 -0
  203. package/dist/charts/area-chart/area-chart.js +14 -0
  204. package/dist/charts/area-chart/index.d.ts +1 -0
  205. package/dist/charts/area-chart/index.js +1 -0
  206. package/dist/charts/bar-chart/bar-chart.d.ts +35 -0
  207. package/dist/charts/bar-chart/bar-chart.js +29 -0
  208. package/dist/charts/bar-chart/bar-race-chart.d.ts +41 -0
  209. package/dist/charts/bar-chart/bar-race-chart.js +252 -0
  210. package/dist/charts/bar-chart/bar-race.d.ts +23 -0
  211. package/dist/charts/bar-chart/bar-race.js +42 -0
  212. package/dist/charts/bar-chart/drilldown-bar-chart.d.ts +18 -0
  213. package/dist/charts/bar-chart/drilldown-bar-chart.js +69 -0
  214. package/dist/charts/bar-chart/index.d.ts +4 -0
  215. package/dist/charts/bar-chart/index.js +4 -0
  216. package/dist/charts/beeswarm-chart/beeswarm-chart.d.ts +53 -0
  217. package/dist/charts/beeswarm-chart/beeswarm-chart.js +356 -0
  218. package/dist/charts/beeswarm-chart/index.d.ts +1 -0
  219. package/dist/charts/beeswarm-chart/index.js +1 -0
  220. package/dist/charts/box-plot/box-plot.d.ts +48 -0
  221. package/dist/charts/box-plot/box-plot.js +330 -0
  222. package/dist/charts/box-plot/index.d.ts +1 -0
  223. package/dist/charts/box-plot/index.js +1 -0
  224. package/dist/charts/bubble-chart/bubble-chart.d.ts +8 -0
  225. package/dist/charts/bubble-chart/bubble-chart.js +9 -0
  226. package/dist/charts/bubble-chart/index.d.ts +1 -0
  227. package/dist/charts/bubble-chart/index.js +1 -0
  228. package/dist/charts/bubble-map/bubble-map.d.ts +94 -0
  229. package/dist/charts/bubble-map/bubble-map.js +520 -0
  230. package/dist/charts/bubble-map/index.d.ts +1 -0
  231. package/dist/charts/bubble-map/index.js +1 -0
  232. package/dist/charts/bullet-chart/bullet-chart.d.ts +49 -0
  233. package/dist/charts/bullet-chart/bullet-chart.js +478 -0
  234. package/dist/charts/bullet-chart/bullet-layout.d.ts +21 -0
  235. package/dist/charts/bullet-chart/bullet-layout.js +30 -0
  236. package/dist/charts/bullet-chart/index.d.ts +2 -0
  237. package/dist/charts/bullet-chart/index.js +2 -0
  238. package/dist/charts/candlestick-chart/candlestick-chart.d.ts +59 -0
  239. package/dist/charts/candlestick-chart/candlestick-chart.js +514 -0
  240. package/dist/charts/candlestick-chart/index.d.ts +1 -0
  241. package/dist/charts/candlestick-chart/index.js +1 -0
  242. package/dist/charts/chart-grid/chart-grid.d.ts +72 -0
  243. package/dist/charts/chart-grid/chart-grid.js +114 -0
  244. package/dist/charts/chart-grid/grid-math.d.ts +26 -0
  245. package/dist/charts/chart-grid/grid-math.js +37 -0
  246. package/dist/charts/chart-grid/index.d.ts +2 -0
  247. package/dist/charts/chart-grid/index.js +2 -0
  248. package/dist/charts/chord-chart/chord-chart.d.ts +59 -0
  249. package/dist/charts/chord-chart/chord-chart.js +427 -0
  250. package/dist/charts/chord-chart/index.d.ts +1 -0
  251. package/dist/charts/chord-chart/index.js +1 -0
  252. package/dist/charts/choropleth-map/choropleth-drilldown.d.ts +36 -0
  253. package/dist/charts/choropleth-map/choropleth-drilldown.js +60 -0
  254. package/dist/charts/choropleth-map/choropleth-map.d.ts +69 -0
  255. package/dist/charts/choropleth-map/choropleth-map.js +304 -0
  256. package/dist/charts/choropleth-map/index.d.ts +2 -0
  257. package/dist/charts/choropleth-map/index.js +2 -0
  258. package/dist/charts/combo-chart/combo-chart.d.ts +15 -0
  259. package/dist/charts/combo-chart/combo-chart.js +14 -0
  260. package/dist/charts/combo-chart/index.d.ts +1 -0
  261. package/dist/charts/combo-chart/index.js +1 -0
  262. package/dist/charts/core/a11y.d.ts +4 -0
  263. package/dist/charts/core/a11y.js +4 -0
  264. package/dist/charts/core/axes.d.ts +41 -0
  265. package/dist/charts/core/axes.js +128 -0
  266. package/dist/charts/core/breadcrumb.d.ts +17 -0
  267. package/dist/charts/core/breadcrumb.js +43 -0
  268. package/dist/charts/core/cartesian/annotations.d.ts +10 -0
  269. package/dist/charts/core/cartesian/annotations.js +139 -0
  270. package/dist/charts/core/cartesian/axis-stack.d.ts +43 -0
  271. package/dist/charts/core/cartesian/axis-stack.js +40 -0
  272. package/dist/charts/core/cartesian/brush.d.ts +31 -0
  273. package/dist/charts/core/cartesian/brush.js +211 -0
  274. package/dist/charts/core/cartesian/cartesian-chart.d.ts +8 -0
  275. package/dist/charts/core/cartesian/cartesian-chart.js +1068 -0
  276. package/dist/charts/core/cartesian/category-group-axis.d.ts +6 -0
  277. package/dist/charts/core/cartesian/category-group-axis.js +39 -0
  278. package/dist/charts/core/cartesian/category-groups.d.ts +15 -0
  279. package/dist/charts/core/cartesian/category-groups.js +23 -0
  280. package/dist/charts/core/cartesian/ink-layer.d.ts +63 -0
  281. package/dist/charts/core/cartesian/ink-layer.js +371 -0
  282. package/dist/charts/core/cartesian/layout.d.ts +164 -0
  283. package/dist/charts/core/cartesian/layout.js +550 -0
  284. package/dist/charts/core/cartesian/marks.d.ts +33 -0
  285. package/dist/charts/core/cartesian/marks.js +110 -0
  286. package/dist/charts/core/cartesian/sync-store.d.ts +34 -0
  287. package/dist/charts/core/cartesian/sync-store.js +61 -0
  288. package/dist/charts/core/cartesian/sync.d.ts +20 -0
  289. package/dist/charts/core/cartesian/sync.js +50 -0
  290. package/dist/charts/core/cartesian/types.d.ts +156 -0
  291. package/dist/charts/core/cartesian/types.js +0 -0
  292. package/dist/charts/core/category-axis.d.ts +37 -0
  293. package/dist/charts/core/category-axis.js +78 -0
  294. package/dist/charts/core/category-explorer.d.ts +48 -0
  295. package/dist/charts/core/category-explorer.js +88 -0
  296. package/dist/charts/core/chart-frame.d.ts +40 -0
  297. package/dist/charts/core/chart-frame.js +63 -0
  298. package/dist/charts/core/chart-states.d.ts +16 -0
  299. package/dist/charts/core/chart-states.js +79 -0
  300. package/dist/charts/core/chord.d.ts +59 -0
  301. package/dist/charts/core/chord.js +104 -0
  302. package/dist/charts/core/color-scale.d.ts +50 -0
  303. package/dist/charts/core/color-scale.js +59 -0
  304. package/dist/charts/core/data-table.d.ts +17 -0
  305. package/dist/charts/core/data-table.js +28 -0
  306. package/dist/charts/core/distribution/brush.d.ts +44 -0
  307. package/dist/charts/core/distribution/brush.js +89 -0
  308. package/dist/charts/core/distribution/dot-stack.d.ts +39 -0
  309. package/dist/charts/core/distribution/dot-stack.js +40 -0
  310. package/dist/charts/core/distribution/explorer.d.ts +23 -0
  311. package/dist/charts/core/distribution/explorer.js +73 -0
  312. package/dist/charts/core/distribution/geometry.d.ts +38 -0
  313. package/dist/charts/core/distribution/geometry.js +37 -0
  314. package/dist/charts/core/distribution/index.d.ts +11 -0
  315. package/dist/charts/core/distribution/index.js +11 -0
  316. package/dist/charts/core/distribution/labels.d.ts +28 -0
  317. package/dist/charts/core/distribution/labels.js +29 -0
  318. package/dist/charts/core/distribution/layout.d.ts +103 -0
  319. package/dist/charts/core/distribution/layout.js +183 -0
  320. package/dist/charts/core/distribution/marks.d.ts +57 -0
  321. package/dist/charts/core/distribution/marks.js +269 -0
  322. package/dist/charts/core/distribution/observations.d.ts +19 -0
  323. package/dist/charts/core/distribution/observations.js +46 -0
  324. package/dist/charts/core/distribution/point-index.d.ts +20 -0
  325. package/dist/charts/core/distribution/point-index.js +47 -0
  326. package/dist/charts/core/distribution/points.d.ts +15 -0
  327. package/dist/charts/core/distribution/points.js +18 -0
  328. package/dist/charts/core/distribution/stats.d.ts +132 -0
  329. package/dist/charts/core/distribution/stats.js +281 -0
  330. package/dist/charts/core/distribution/use-legend.d.ts +31 -0
  331. package/dist/charts/core/distribution/use-legend.js +43 -0
  332. package/dist/charts/core/distribution/xy-points.d.ts +109 -0
  333. package/dist/charts/core/distribution/xy-points.js +968 -0
  334. package/dist/charts/core/downsample.d.ts +21 -0
  335. package/dist/charts/core/downsample.js +65 -0
  336. package/dist/charts/core/drawing-height.d.ts +14 -0
  337. package/dist/charts/core/drawing-height.js +11 -0
  338. package/dist/charts/core/export-menu.d.ts +20 -0
  339. package/dist/charts/core/export-menu.js +52 -0
  340. package/dist/charts/core/export.d.ts +45 -0
  341. package/dist/charts/core/export.js +162 -0
  342. package/dist/charts/core/format.d.ts +48 -0
  343. package/dist/charts/core/format.js +88 -0
  344. package/dist/charts/core/gantt.d.ts +136 -0
  345. package/dist/charts/core/gantt.js +362 -0
  346. package/dist/charts/core/hierarchy-chart.d.ts +76 -0
  347. package/dist/charts/core/hierarchy-chart.js +158 -0
  348. package/dist/charts/core/hierarchy.d.ts +118 -0
  349. package/dist/charts/core/hierarchy.js +229 -0
  350. package/dist/charts/core/index.d.ts +54 -0
  351. package/dist/charts/core/index.js +54 -0
  352. package/dist/charts/core/indicators.d.ts +55 -0
  353. package/dist/charts/core/indicators.js +125 -0
  354. package/dist/charts/core/ink.d.ts +127 -0
  355. package/dist/charts/core/ink.js +178 -0
  356. package/dist/charts/core/legend.d.ts +23 -0
  357. package/dist/charts/core/legend.js +79 -0
  358. package/dist/charts/core/motion.d.ts +33 -0
  359. package/dist/charts/core/motion.js +81 -0
  360. package/dist/charts/core/palette.d.ts +38 -0
  361. package/dist/charts/core/palette.js +52 -0
  362. package/dist/charts/core/path.d.ts +40 -0
  363. package/dist/charts/core/path.js +141 -0
  364. package/dist/charts/core/pattern-defs.d.ts +15 -0
  365. package/dist/charts/core/pattern-defs.js +83 -0
  366. package/dist/charts/core/patterns.d.ts +49 -0
  367. package/dist/charts/core/patterns.js +93 -0
  368. package/dist/charts/core/plot-overlay.d.ts +36 -0
  369. package/dist/charts/core/plot-overlay.js +68 -0
  370. package/dist/charts/core/polar.d.ts +111 -0
  371. package/dist/charts/core/polar.js +173 -0
  372. package/dist/charts/core/sankey.d.ts +168 -0
  373. package/dist/charts/core/sankey.js +354 -0
  374. package/dist/charts/core/scales/band.d.ts +34 -0
  375. package/dist/charts/core/scales/band.js +58 -0
  376. package/dist/charts/core/scales/index.d.ts +4 -0
  377. package/dist/charts/core/scales/index.js +4 -0
  378. package/dist/charts/core/scales/linear.d.ts +27 -0
  379. package/dist/charts/core/scales/linear.js +73 -0
  380. package/dist/charts/core/scales/log.d.ts +8 -0
  381. package/dist/charts/core/scales/log.js +30 -0
  382. package/dist/charts/core/scales/time.d.ts +26 -0
  383. package/dist/charts/core/scales/time.js +98 -0
  384. package/dist/charts/core/shape-morph.d.ts +29 -0
  385. package/dist/charts/core/shape-morph.js +60 -0
  386. package/dist/charts/core/stack.d.ts +14 -0
  387. package/dist/charts/core/stack.js +27 -0
  388. package/dist/charts/core/time-range.d.ts +23 -0
  389. package/dist/charts/core/time-range.js +27 -0
  390. package/dist/charts/core/tooltip.d.ts +38 -0
  391. package/dist/charts/core/tooltip.js +83 -0
  392. package/dist/charts/core/tree-fit.d.ts +29 -0
  393. package/dist/charts/core/tree-fit.js +53 -0
  394. package/dist/charts/core/tree-layout.d.ts +85 -0
  395. package/dist/charts/core/tree-layout.js +237 -0
  396. package/dist/charts/core/tree-radial.d.ts +59 -0
  397. package/dist/charts/core/tree-radial.js +209 -0
  398. package/dist/charts/core/types.d.ts +106 -0
  399. package/dist/charts/core/types.js +18 -0
  400. package/dist/charts/core/unit-glyph.d.ts +40 -0
  401. package/dist/charts/core/unit-glyph.js +61 -0
  402. package/dist/charts/core/unit-icons.d.ts +132 -0
  403. package/dist/charts/core/unit-icons.js +157 -0
  404. package/dist/charts/core/units.d.ts +101 -0
  405. package/dist/charts/core/units.js +161 -0
  406. package/dist/charts/core/use-canvas.d.ts +24 -0
  407. package/dist/charts/core/use-canvas.js +50 -0
  408. package/dist/charts/core/use-chart-size.d.ts +19 -0
  409. package/dist/charts/core/use-chart-size.js +43 -0
  410. package/dist/charts/core/use-drill.d.ts +26 -0
  411. package/dist/charts/core/use-drill.js +67 -0
  412. package/dist/charts/core/use-index-navigation.d.ts +30 -0
  413. package/dist/charts/core/use-index-navigation.js +47 -0
  414. package/dist/charts/core/use-pan-zoom.d.ts +56 -0
  415. package/dist/charts/core/use-pan-zoom.js +163 -0
  416. package/dist/charts/core/use-series-state.d.ts +13 -0
  417. package/dist/charts/core/use-series-state.js +26 -0
  418. package/dist/charts/crossfilter/crossfilter-engine.d.ts +54 -0
  419. package/dist/charts/crossfilter/crossfilter-engine.js +125 -0
  420. package/dist/charts/crossfilter/crossfilter.d.ts +140 -0
  421. package/dist/charts/crossfilter/crossfilter.js +333 -0
  422. package/dist/charts/crossfilter/index.d.ts +2 -0
  423. package/dist/charts/crossfilter/index.js +2 -0
  424. package/dist/charts/donut-chart/center-fit.d.ts +14 -0
  425. package/dist/charts/donut-chart/center-fit.js +29 -0
  426. package/dist/charts/donut-chart/donut-chart.d.ts +73 -0
  427. package/dist/charts/donut-chart/donut-chart.js +639 -0
  428. package/dist/charts/donut-chart/index.d.ts +1 -0
  429. package/dist/charts/donut-chart/index.js +1 -0
  430. package/dist/charts/dumbbell-chart/dumbbell-chart.d.ts +68 -0
  431. package/dist/charts/dumbbell-chart/dumbbell-chart.js +585 -0
  432. package/dist/charts/dumbbell-chart/index.d.ts +1 -0
  433. package/dist/charts/dumbbell-chart/index.js +1 -0
  434. package/dist/charts/fill-gauge-chart/fill-gauge-chart.d.ts +42 -0
  435. package/dist/charts/fill-gauge-chart/fill-gauge-chart.js +259 -0
  436. package/dist/charts/fill-gauge-chart/index.d.ts +1 -0
  437. package/dist/charts/fill-gauge-chart/index.js +1 -0
  438. package/dist/charts/funnel-chart/funnel-chart.d.ts +48 -0
  439. package/dist/charts/funnel-chart/funnel-chart.js +423 -0
  440. package/dist/charts/funnel-chart/index.d.ts +1 -0
  441. package/dist/charts/funnel-chart/index.js +1 -0
  442. package/dist/charts/gantt-chart/gantt-chart.d.ts +120 -0
  443. package/dist/charts/gantt-chart/gantt-chart.js +1144 -0
  444. package/dist/charts/gantt-chart/gantt-export.d.ts +26 -0
  445. package/dist/charts/gantt-chart/gantt-export.js +103 -0
  446. package/dist/charts/gantt-chart/gantt-marks.d.ts +125 -0
  447. package/dist/charts/gantt-chart/gantt-marks.js +602 -0
  448. package/dist/charts/gantt-chart/index.d.ts +1 -0
  449. package/dist/charts/gantt-chart/index.js +1 -0
  450. package/dist/charts/gauge-chart/gauge-chart.d.ts +71 -0
  451. package/dist/charts/gauge-chart/gauge-chart.js +458 -0
  452. package/dist/charts/gauge-chart/index.d.ts +1 -0
  453. package/dist/charts/gauge-chart/index.js +1 -0
  454. package/dist/charts/globe-chart/globe-chart.d.ts +75 -0
  455. package/dist/charts/globe-chart/globe-chart.js +618 -0
  456. package/dist/charts/globe-chart/index.d.ts +1 -0
  457. package/dist/charts/globe-chart/index.js +1 -0
  458. package/dist/charts/heatmap-chart/calendar-heatmap.d.ts +47 -0
  459. package/dist/charts/heatmap-chart/calendar-heatmap.js +159 -0
  460. package/dist/charts/heatmap-chart/calendar-layout.d.ts +27 -0
  461. package/dist/charts/heatmap-chart/calendar-layout.js +47 -0
  462. package/dist/charts/heatmap-chart/datetime-columns.d.ts +8 -0
  463. package/dist/charts/heatmap-chart/datetime-columns.js +29 -0
  464. package/dist/charts/heatmap-chart/drilldown-heatmap.d.ts +33 -0
  465. package/dist/charts/heatmap-chart/drilldown-heatmap.js +58 -0
  466. package/dist/charts/heatmap-chart/heatmap-chart.d.ts +59 -0
  467. package/dist/charts/heatmap-chart/heatmap-chart.js +127 -0
  468. package/dist/charts/heatmap-chart/heatmap-grid.d.ts +75 -0
  469. package/dist/charts/heatmap-chart/heatmap-grid.js +494 -0
  470. package/dist/charts/heatmap-chart/heatmap-scale.d.ts +19 -0
  471. package/dist/charts/heatmap-chart/heatmap-scale.js +38 -0
  472. package/dist/charts/heatmap-chart/hex-layout.d.ts +35 -0
  473. package/dist/charts/heatmap-chart/hex-layout.js +58 -0
  474. package/dist/charts/heatmap-chart/index.d.ts +9 -0
  475. package/dist/charts/heatmap-chart/index.js +12 -0
  476. package/dist/charts/hex-tile-map/hex-layout.d.ts +40 -0
  477. package/dist/charts/hex-tile-map/hex-layout.js +96 -0
  478. package/dist/charts/hex-tile-map/hex-tile-map.d.ts +41 -0
  479. package/dist/charts/hex-tile-map/hex-tile-map.js +307 -0
  480. package/dist/charts/hex-tile-map/index.d.ts +2 -0
  481. package/dist/charts/hex-tile-map/index.js +2 -0
  482. package/dist/charts/hierarchy-morph/hierarchy-morph.d.ts +47 -0
  483. package/dist/charts/hierarchy-morph/hierarchy-morph.js +355 -0
  484. package/dist/charts/hierarchy-morph/index.d.ts +1 -0
  485. package/dist/charts/hierarchy-morph/index.js +1 -0
  486. package/dist/charts/histogram/histogram.d.ts +55 -0
  487. package/dist/charts/histogram/histogram.js +444 -0
  488. package/dist/charts/histogram/index.d.ts +1 -0
  489. package/dist/charts/histogram/index.js +1 -0
  490. package/dist/charts/icicle-chart/icicle-chart.d.ts +41 -0
  491. package/dist/charts/icicle-chart/icicle-chart.js +360 -0
  492. package/dist/charts/icicle-chart/index.d.ts +1 -0
  493. package/dist/charts/icicle-chart/index.js +1 -0
  494. package/dist/charts/index.d.ts +50 -0
  495. package/dist/charts/index.js +50 -0
  496. package/dist/charts/isotype-rows/index.d.ts +1 -0
  497. package/dist/charts/isotype-rows/index.js +1 -0
  498. package/dist/charts/isotype-rows/isotype-rows.d.ts +42 -0
  499. package/dist/charts/isotype-rows/isotype-rows.js +274 -0
  500. package/dist/charts/line-chart/index.d.ts +2 -0
  501. package/dist/charts/line-chart/index.js +2 -0
  502. package/dist/charts/line-chart/line-chart.d.ts +15 -0
  503. package/dist/charts/line-chart/line-chart.js +19 -0
  504. package/dist/charts/line-chart/line-drilldown.d.ts +18 -0
  505. package/dist/charts/line-chart/line-drilldown.js +51 -0
  506. package/dist/charts/lollipop-chart/index.d.ts +1 -0
  507. package/dist/charts/lollipop-chart/index.js +1 -0
  508. package/dist/charts/lollipop-chart/lollipop-chart.d.ts +45 -0
  509. package/dist/charts/lollipop-chart/lollipop-chart.js +325 -0
  510. package/dist/charts/maps/data/peru/index.d.ts +2 -0
  511. package/dist/charts/maps/data/peru/index.js +4 -0
  512. package/dist/charts/maps/data/world/index.d.ts +2 -0
  513. package/dist/charts/maps/data/world/index.js +4 -0
  514. package/dist/charts/maps/geo/build-topology.d.ts +20 -0
  515. package/dist/charts/maps/geo/build-topology.js +175 -0
  516. package/dist/charts/maps/geo/cluster.d.ts +22 -0
  517. package/dist/charts/maps/geo/cluster.js +58 -0
  518. package/dist/charts/maps/geo/geo-path.d.ts +20 -0
  519. package/dist/charts/maps/geo/geo-path.js +132 -0
  520. package/dist/charts/maps/geo/globe.d.ts +72 -0
  521. package/dist/charts/maps/geo/globe.js +223 -0
  522. package/dist/charts/maps/geo/index.d.ts +7 -0
  523. package/dist/charts/maps/geo/index.js +7 -0
  524. package/dist/charts/maps/geo/map-data-table.d.ts +25 -0
  525. package/dist/charts/maps/geo/map-data-table.js +99 -0
  526. package/dist/charts/maps/geo/map-labels.d.ts +33 -0
  527. package/dist/charts/maps/geo/map-labels.js +30 -0
  528. package/dist/charts/maps/geo/map-legend.d.ts +19 -0
  529. package/dist/charts/maps/geo/map-legend.js +70 -0
  530. package/dist/charts/maps/geo/map-viewport.d.ts +69 -0
  531. package/dist/charts/maps/geo/map-viewport.js +326 -0
  532. package/dist/charts/maps/geo/match.d.ts +9 -0
  533. package/dist/charts/maps/geo/match.js +12 -0
  534. package/dist/charts/maps/geo/projection.d.ts +47 -0
  535. package/dist/charts/maps/geo/projection.js +118 -0
  536. package/dist/charts/maps/geo/pure.d.ts +11 -0
  537. package/dist/charts/maps/geo/pure.js +11 -0
  538. package/dist/charts/maps/geo/routes.d.ts +19 -0
  539. package/dist/charts/maps/geo/routes.js +63 -0
  540. package/dist/charts/maps/geo/simplify.d.ts +11 -0
  541. package/dist/charts/maps/geo/simplify.js +49 -0
  542. package/dist/charts/maps/geo/topojson.d.ts +18 -0
  543. package/dist/charts/maps/geo/topojson.js +74 -0
  544. package/dist/charts/maps/geo/types.d.ts +54 -0
  545. package/dist/charts/maps/geo/types.js +0 -0
  546. package/dist/charts/maps/geo/use-map-geometry.d.ts +31 -0
  547. package/dist/charts/maps/geo/use-map-geometry.js +100 -0
  548. package/dist/charts/maps/geo/use-map-view.d.ts +44 -0
  549. package/dist/charts/maps/geo/use-map-view.js +199 -0
  550. package/dist/charts/maps/geo/view.d.ts +30 -0
  551. package/dist/charts/maps/geo/view.js +55 -0
  552. package/dist/charts/maps/index.d.ts +1 -0
  553. package/dist/charts/maps/index.js +1 -0
  554. package/dist/charts/parliament-chart/index.d.ts +1 -0
  555. package/dist/charts/parliament-chart/index.js +1 -0
  556. package/dist/charts/parliament-chart/parliament-chart.d.ts +40 -0
  557. package/dist/charts/parliament-chart/parliament-chart.js +325 -0
  558. package/dist/charts/pictogram-chart/index.d.ts +1 -0
  559. package/dist/charts/pictogram-chart/index.js +1 -0
  560. package/dist/charts/pictogram-chart/pictogram-chart.d.ts +43 -0
  561. package/dist/charts/pictogram-chart/pictogram-chart.js +319 -0
  562. package/dist/charts/pie-chart/index.d.ts +1 -0
  563. package/dist/charts/pie-chart/index.js +1 -0
  564. package/dist/charts/pie-chart/pie-chart.d.ts +7 -0
  565. package/dist/charts/pie-chart/pie-chart.js +9 -0
  566. package/dist/charts/polar-area-chart/index.d.ts +1 -0
  567. package/dist/charts/polar-area-chart/index.js +1 -0
  568. package/dist/charts/polar-area-chart/polar-area-chart.d.ts +46 -0
  569. package/dist/charts/polar-area-chart/polar-area-chart.js +389 -0
  570. package/dist/charts/progress-units/index.d.ts +1 -0
  571. package/dist/charts/progress-units/index.js +1 -0
  572. package/dist/charts/progress-units/progress-units.d.ts +39 -0
  573. package/dist/charts/progress-units/progress-units.js +154 -0
  574. package/dist/charts/radar-chart/index.d.ts +1 -0
  575. package/dist/charts/radar-chart/index.js +1 -0
  576. package/dist/charts/radar-chart/radar-chart.d.ts +44 -0
  577. package/dist/charts/radar-chart/radar-chart.js +434 -0
  578. package/dist/charts/radial-bar-chart/index.d.ts +1 -0
  579. package/dist/charts/radial-bar-chart/index.js +1 -0
  580. package/dist/charts/radial-bar-chart/radial-bar-chart.d.ts +53 -0
  581. package/dist/charts/radial-bar-chart/radial-bar-chart.js +430 -0
  582. package/dist/charts/raincloud-chart/index.d.ts +1 -0
  583. package/dist/charts/raincloud-chart/index.js +1 -0
  584. package/dist/charts/raincloud-chart/raincloud-chart.d.ts +39 -0
  585. package/dist/charts/raincloud-chart/raincloud-chart.js +363 -0
  586. package/dist/charts/range-area-chart/index.d.ts +1 -0
  587. package/dist/charts/range-area-chart/index.js +1 -0
  588. package/dist/charts/range-area-chart/range-area-chart.d.ts +52 -0
  589. package/dist/charts/range-area-chart/range-area-chart.js +414 -0
  590. package/dist/charts/sankey-chart/alluvial-chart.d.ts +11 -0
  591. package/dist/charts/sankey-chart/alluvial-chart.js +45 -0
  592. package/dist/charts/sankey-chart/index.d.ts +2 -0
  593. package/dist/charts/sankey-chart/index.js +2 -0
  594. package/dist/charts/sankey-chart/sankey-chart.d.ts +91 -0
  595. package/dist/charts/sankey-chart/sankey-chart.js +868 -0
  596. package/dist/charts/scatter-chart/index.d.ts +1 -0
  597. package/dist/charts/scatter-chart/index.js +1 -0
  598. package/dist/charts/scatter-chart/scatter-chart.d.ts +7 -0
  599. package/dist/charts/scatter-chart/scatter-chart.js +9 -0
  600. package/dist/charts/slope-chart/index.d.ts +2 -0
  601. package/dist/charts/slope-chart/index.js +2 -0
  602. package/dist/charts/slope-chart/label-layout.d.ts +10 -0
  603. package/dist/charts/slope-chart/label-layout.js +35 -0
  604. package/dist/charts/slope-chart/slope-chart.d.ts +43 -0
  605. package/dist/charts/slope-chart/slope-chart.js +421 -0
  606. package/dist/charts/sparkline/index.d.ts +1 -0
  607. package/dist/charts/sparkline/index.js +1 -0
  608. package/dist/charts/sparkline/sparkline.d.ts +30 -0
  609. package/dist/charts/sparkline/sparkline.js +227 -0
  610. package/dist/charts/stock-chart/index.d.ts +1 -0
  611. package/dist/charts/stock-chart/index.js +1 -0
  612. package/dist/charts/stock-chart/stock-chart.d.ts +103 -0
  613. package/dist/charts/stock-chart/stock-chart.js +552 -0
  614. package/dist/charts/stock-chart/stock-drawing-layer.d.ts +66 -0
  615. package/dist/charts/stock-chart/stock-drawing-layer.js +338 -0
  616. package/dist/charts/stock-chart/stock-drawings.d.ts +68 -0
  617. package/dist/charts/stock-chart/stock-drawings.js +74 -0
  618. package/dist/charts/stock-chart/stock-model.d.ts +87 -0
  619. package/dist/charts/stock-chart/stock-model.js +83 -0
  620. package/dist/charts/stock-chart/stock-plot.d.ts +84 -0
  621. package/dist/charts/stock-chart/stock-plot.js +1005 -0
  622. package/dist/charts/stock-chart/stock-stats.d.ts +49 -0
  623. package/dist/charts/stock-chart/stock-stats.js +68 -0
  624. package/dist/charts/stock-chart/stock-transforms.d.ts +19 -0
  625. package/dist/charts/stock-chart/stock-transforms.js +72 -0
  626. package/dist/charts/stock-chart/use-stock-stream.d.ts +16 -0
  627. package/dist/charts/stock-chart/use-stock-stream.js +16 -0
  628. package/dist/charts/stream-graph/index.d.ts +2 -0
  629. package/dist/charts/stream-graph/index.js +2 -0
  630. package/dist/charts/stream-graph/stream-graph.d.ts +38 -0
  631. package/dist/charts/stream-graph/stream-graph.js +415 -0
  632. package/dist/charts/stream-graph/stream-layout.d.ts +34 -0
  633. package/dist/charts/stream-graph/stream-layout.js +89 -0
  634. package/dist/charts/sunburst-chart/index.d.ts +1 -0
  635. package/dist/charts/sunburst-chart/index.js +1 -0
  636. package/dist/charts/sunburst-chart/sunburst-chart.d.ts +44 -0
  637. package/dist/charts/sunburst-chart/sunburst-chart.js +437 -0
  638. package/dist/charts/timeline-chart/index.d.ts +2 -0
  639. package/dist/charts/timeline-chart/index.js +2 -0
  640. package/dist/charts/timeline-chart/timeline-chart.d.ts +70 -0
  641. package/dist/charts/timeline-chart/timeline-chart.js +524 -0
  642. package/dist/charts/timeline-chart/timeline-lanes.d.ts +43 -0
  643. package/dist/charts/timeline-chart/timeline-lanes.js +65 -0
  644. package/dist/charts/tree-chart/index.d.ts +3 -0
  645. package/dist/charts/tree-chart/index.js +3 -0
  646. package/dist/charts/tree-chart/tree-card.d.ts +37 -0
  647. package/dist/charts/tree-chart/tree-card.js +94 -0
  648. package/dist/charts/tree-chart/tree-chart.d.ts +118 -0
  649. package/dist/charts/tree-chart/tree-chart.js +850 -0
  650. package/dist/charts/tree-chart/tree-model.d.ts +87 -0
  651. package/dist/charts/tree-chart/tree-model.js +116 -0
  652. package/dist/charts/tree-chart/use-tree-transition.d.ts +16 -0
  653. package/dist/charts/tree-chart/use-tree-transition.js +84 -0
  654. package/dist/charts/treemap-chart/index.d.ts +1 -0
  655. package/dist/charts/treemap-chart/index.js +1 -0
  656. package/dist/charts/treemap-chart/treemap-chart.d.ts +44 -0
  657. package/dist/charts/treemap-chart/treemap-chart.js +347 -0
  658. package/dist/charts/violin-chart/index.d.ts +1 -0
  659. package/dist/charts/violin-chart/index.js +1 -0
  660. package/dist/charts/violin-chart/violin-chart.d.ts +53 -0
  661. package/dist/charts/violin-chart/violin-chart.js +406 -0
  662. package/dist/charts/waffle-chart/index.d.ts +1 -0
  663. package/dist/charts/waffle-chart/index.js +1 -0
  664. package/dist/charts/waffle-chart/waffle-chart.d.ts +40 -0
  665. package/dist/charts/waffle-chart/waffle-chart.js +273 -0
  666. package/dist/charts/waterfall-chart/index.d.ts +2 -0
  667. package/dist/charts/waterfall-chart/index.js +2 -0
  668. package/dist/charts/waterfall-chart/waterfall-chart.d.ts +38 -0
  669. package/dist/charts/waterfall-chart/waterfall-chart.js +531 -0
  670. package/dist/charts/waterfall-chart/waterfall-layout.d.ts +51 -0
  671. package/dist/charts/waterfall-chart/waterfall-layout.js +54 -0
  672. package/dist/containers/action-bar/action-bar.d.ts +18 -0
  673. package/dist/containers/action-bar/action-bar.js +63 -0
  674. package/dist/containers/action-bar/index.d.ts +1 -0
  675. package/dist/containers/action-bar/index.js +4 -0
  676. package/dist/containers/banner/banner.d.ts +10 -0
  677. package/dist/containers/banner/banner.js +49 -0
  678. package/dist/containers/banner/index.d.ts +1 -0
  679. package/dist/containers/banner/index.js +1 -0
  680. package/dist/containers/card/card.d.ts +6 -0
  681. package/dist/containers/card/card.js +47 -0
  682. package/dist/containers/card/index.d.ts +1 -0
  683. package/dist/containers/card/index.js +1 -0
  684. package/dist/containers/carousel/carousel.d.ts +19 -0
  685. package/dist/containers/carousel/carousel.js +178 -0
  686. package/dist/containers/carousel/index.d.ts +2 -0
  687. package/dist/containers/carousel/index.js +7 -0
  688. package/dist/containers/carousel/model.d.ts +4 -0
  689. package/dist/containers/carousel/model.js +22 -0
  690. package/dist/containers/cell/cell.d.ts +44 -0
  691. package/dist/containers/cell/cell.js +83 -0
  692. package/dist/containers/cell/index.d.ts +1 -0
  693. package/dist/containers/cell/index.js +6 -0
  694. package/dist/containers/cols/cols.d.ts +15 -0
  695. package/dist/containers/cols/cols.js +46 -0
  696. package/dist/containers/cols/index.d.ts +1 -0
  697. package/dist/containers/cols/index.js +1 -0
  698. package/dist/containers/comment/comment.d.ts +14 -0
  699. package/dist/containers/comment/comment.js +44 -0
  700. package/dist/containers/comment/index.d.ts +1 -0
  701. package/dist/containers/comment/index.js +1 -0
  702. package/dist/containers/data-grid/chart-panel.d.ts +24 -0
  703. package/dist/containers/data-grid/chart-panel.js +114 -0
  704. package/dist/containers/data-grid/chart-range.d.ts +46 -0
  705. package/dist/containers/data-grid/chart-range.js +73 -0
  706. package/dist/containers/data-grid/column-groups.d.ts +15 -0
  707. package/dist/containers/data-grid/column-groups.js +29 -0
  708. package/dist/containers/data-grid/column-menu.d.ts +28 -0
  709. package/dist/containers/data-grid/column-menu.js +361 -0
  710. package/dist/containers/data-grid/data-grid.d.ts +199 -0
  711. package/dist/containers/data-grid/data-grid.demos.d.ts +15 -0
  712. package/dist/containers/data-grid/data-grid.demos.js +578 -0
  713. package/dist/containers/data-grid/data-grid.fixtures.d.ts +61 -0
  714. package/dist/containers/data-grid/data-grid.fixtures.js +316 -0
  715. package/dist/containers/data-grid/data-grid.js +2850 -0
  716. package/dist/containers/data-grid/data-grid.samples.d.ts +81 -0
  717. package/dist/containers/data-grid/data-grid.samples.js +345 -0
  718. package/dist/containers/data-grid/data-source.d.ts +56 -0
  719. package/dist/containers/data-grid/data-source.js +90 -0
  720. package/dist/containers/data-grid/fill-series.d.ts +97 -0
  721. package/dist/containers/data-grid/fill-series.js +168 -0
  722. package/dist/containers/data-grid/filter-builder-editor.d.ts +18 -0
  723. package/dist/containers/data-grid/filter-builder-editor.js +207 -0
  724. package/dist/containers/data-grid/filter-builder.d.ts +47 -0
  725. package/dist/containers/data-grid/filter-builder.js +227 -0
  726. package/dist/containers/data-grid/formula-bar.d.ts +18 -0
  727. package/dist/containers/data-grid/formula-bar.js +68 -0
  728. package/dist/containers/data-grid/formula-sheet.d.ts +48 -0
  729. package/dist/containers/data-grid/formula-sheet.js +175 -0
  730. package/dist/containers/data-grid/formula.d.ts +86 -0
  731. package/dist/containers/data-grid/formula.js +580 -0
  732. package/dist/containers/data-grid/grid-edit.d.ts +52 -0
  733. package/dist/containers/data-grid/grid-edit.js +99 -0
  734. package/dist/containers/data-grid/grid-toolbar.d.ts +53 -0
  735. package/dist/containers/data-grid/grid-toolbar.js +266 -0
  736. package/dist/containers/data-grid/history.d.ts +61 -0
  737. package/dist/containers/data-grid/history.js +84 -0
  738. package/dist/containers/data-grid/index.d.ts +18 -0
  739. package/dist/containers/data-grid/index.js +18 -0
  740. package/dist/containers/data-grid/labels.d.ts +141 -0
  741. package/dist/containers/data-grid/labels.js +157 -0
  742. package/dist/containers/data-grid/model.d.ts +93 -0
  743. package/dist/containers/data-grid/model.js +481 -0
  744. package/dist/containers/data-grid/pivot.d.ts +36 -0
  745. package/dist/containers/data-grid/pivot.js +126 -0
  746. package/dist/containers/data-grid/range.d.ts +41 -0
  747. package/dist/containers/data-grid/range.js +78 -0
  748. package/dist/containers/data-grid/rows.d.ts +26 -0
  749. package/dist/containers/data-grid/rows.js +67 -0
  750. package/dist/containers/data-grid/spreadsheet.demos.d.ts +75 -0
  751. package/dist/containers/data-grid/spreadsheet.demos.js +423 -0
  752. package/dist/containers/data-grid/tool-panel.d.ts +31 -0
  753. package/dist/containers/data-grid/tool-panel.js +222 -0
  754. package/dist/containers/data-grid/tree.d.ts +25 -0
  755. package/dist/containers/data-grid/tree.js +65 -0
  756. package/dist/containers/data-grid/types.d.ts +154 -0
  757. package/dist/containers/data-grid/types.js +0 -0
  758. package/dist/containers/data-grid/use-data-source.d.ts +31 -0
  759. package/dist/containers/data-grid/use-data-source.js +77 -0
  760. package/dist/containers/data-grid/use-grid-layout.d.ts +25 -0
  761. package/dist/containers/data-grid/use-grid-layout.js +99 -0
  762. package/dist/containers/data-grid/virtual.d.ts +53 -0
  763. package/dist/containers/data-grid/virtual.js +90 -0
  764. package/dist/containers/data-grid/xlsx-export.d.ts +9 -0
  765. package/dist/containers/data-grid/xlsx-export.js +39 -0
  766. package/dist/containers/description-list/description-list.d.ts +8 -0
  767. package/dist/containers/description-list/description-list.js +19 -0
  768. package/dist/containers/description-list/index.d.ts +1 -0
  769. package/dist/containers/description-list/index.js +1 -0
  770. package/dist/containers/facts/facts.d.ts +8 -0
  771. package/dist/containers/facts/facts.js +20 -0
  772. package/dist/containers/facts/index.d.ts +1 -0
  773. package/dist/containers/facts/index.js +1 -0
  774. package/dist/containers/file-tree/file-tree.d.ts +18 -0
  775. package/dist/containers/file-tree/file-tree.js +154 -0
  776. package/dist/containers/file-tree/index.d.ts +2 -0
  777. package/dist/containers/file-tree/index.js +16 -0
  778. package/dist/containers/file-tree/model.d.ts +37 -0
  779. package/dist/containers/file-tree/model.js +83 -0
  780. package/dist/containers/generic-row/generic-row.d.ts +16 -0
  781. package/dist/containers/generic-row/generic-row.js +29 -0
  782. package/dist/containers/generic-row/index.d.ts +1 -0
  783. package/dist/containers/generic-row/index.js +1 -0
  784. package/dist/containers/hbars/hbars.d.ts +57 -0
  785. package/dist/containers/hbars/hbars.js +124 -0
  786. package/dist/containers/hbars/index.d.ts +1 -0
  787. package/dist/containers/hbars/index.js +1 -0
  788. package/dist/containers/highlight-line/highlight-line.d.ts +7 -0
  789. package/dist/containers/highlight-line/highlight-line.js +14 -0
  790. package/dist/containers/highlight-line/index.d.ts +1 -0
  791. package/dist/containers/highlight-line/index.js +1 -0
  792. package/dist/containers/index.d.ts +26 -0
  793. package/dist/containers/index.js +26 -0
  794. package/dist/containers/item-card-group/index.d.ts +1 -0
  795. package/dist/containers/item-card-group/index.js +6 -0
  796. package/dist/containers/item-card-group/item-card-group.d.ts +22 -0
  797. package/dist/containers/item-card-group/item-card-group.js +66 -0
  798. package/dist/containers/kanban/index.d.ts +2 -0
  799. package/dist/containers/kanban/index.js +16 -0
  800. package/dist/containers/kanban/kanban.d.ts +22 -0
  801. package/dist/containers/kanban/kanban.js +329 -0
  802. package/dist/containers/kanban/model.d.ts +49 -0
  803. package/dist/containers/kanban/model.js +77 -0
  804. package/dist/containers/kpi/index.d.ts +1 -0
  805. package/dist/containers/kpi/index.js +1 -0
  806. package/dist/containers/kpi/kpi.d.ts +15 -0
  807. package/dist/containers/kpi/kpi.js +40 -0
  808. package/dist/containers/kpi-group/index.d.ts +1 -0
  809. package/dist/containers/kpi-group/index.js +4 -0
  810. package/dist/containers/kpi-group/kpi-group.d.ts +25 -0
  811. package/dist/containers/kpi-group/kpi-group.js +40 -0
  812. package/dist/containers/page-header/index.d.ts +1 -0
  813. package/dist/containers/page-header/index.js +1 -0
  814. package/dist/containers/page-header/page-header.d.ts +17 -0
  815. package/dist/containers/page-header/page-header.js +42 -0
  816. package/dist/containers/resizable/index.d.ts +2 -0
  817. package/dist/containers/resizable/index.js +14 -0
  818. package/dist/containers/resizable/model.d.ts +18 -0
  819. package/dist/containers/resizable/model.js +62 -0
  820. package/dist/containers/resizable/resizable.d.ts +27 -0
  821. package/dist/containers/resizable/resizable.js +234 -0
  822. package/dist/containers/scrolly/index.d.ts +2 -0
  823. package/dist/containers/scrolly/index.js +9 -0
  824. package/dist/containers/scrolly/scrolly-logic.d.ts +16 -0
  825. package/dist/containers/scrolly/scrolly-logic.js +29 -0
  826. package/dist/containers/scrolly/scrolly.d.ts +45 -0
  827. package/dist/containers/scrolly/scrolly.js +154 -0
  828. package/dist/containers/settings-layout/index.d.ts +1 -0
  829. package/dist/containers/settings-layout/index.js +1 -0
  830. package/dist/containers/settings-layout/settings-layout.d.ts +21 -0
  831. package/dist/containers/settings-layout/settings-layout.js +71 -0
  832. package/dist/containers/settings-row/index.d.ts +1 -0
  833. package/dist/containers/settings-row/index.js +1 -0
  834. package/dist/containers/settings-row/settings-row.d.ts +10 -0
  835. package/dist/containers/settings-row/settings-row.js +31 -0
  836. package/dist/containers/table/index.d.ts +1 -0
  837. package/dist/containers/table/index.js +1 -0
  838. package/dist/containers/table/pin.d.ts +33 -0
  839. package/dist/containers/table/pin.js +56 -0
  840. package/dist/containers/table/table.d.ts +60 -0
  841. package/dist/containers/table/table.js +325 -0
  842. package/dist/containers/timeline/index.d.ts +1 -0
  843. package/dist/containers/timeline/index.js +6 -0
  844. package/dist/containers/timeline/timeline.d.ts +25 -0
  845. package/dist/containers/timeline/timeline.js +56 -0
  846. package/dist/containers/toolbar/index.d.ts +1 -0
  847. package/dist/containers/toolbar/index.js +1 -0
  848. package/dist/containers/toolbar/toolbar.d.ts +7 -0
  849. package/dist/containers/toolbar/toolbar.js +18 -0
  850. package/dist/dashboards/data.d.ts +29 -0
  851. package/dist/dashboards/data.js +42 -0
  852. package/dist/dashboards/inicio-dashboard.d.ts +1 -0
  853. package/dist/dashboards/inicio-dashboard.js +200 -0
  854. package/dist/dashboards/layout.d.ts +33 -0
  855. package/dist/dashboards/layout.js +55 -0
  856. package/dist/forms/accent-picker/accent-picker.d.ts +20 -0
  857. package/dist/forms/accent-picker/accent-picker.js +51 -0
  858. package/dist/forms/accent-picker/index.d.ts +1 -0
  859. package/dist/forms/accent-picker/index.js +6 -0
  860. package/dist/forms/checkbox-button-group/checkbox-button-group.d.ts +16 -0
  861. package/dist/forms/checkbox-button-group/checkbox-button-group.js +52 -0
  862. package/dist/forms/checkbox-button-group/index.d.ts +1 -0
  863. package/dist/forms/checkbox-button-group/index.js +6 -0
  864. package/dist/forms/dropzone/dropzone.d.ts +19 -0
  865. package/dist/forms/dropzone/dropzone.js +103 -0
  866. package/dist/forms/dropzone/index.d.ts +1 -0
  867. package/dist/forms/dropzone/index.js +6 -0
  868. package/dist/forms/emoji-picker/emoji-data.d.ts +14 -0
  869. package/dist/forms/emoji-picker/emoji-data.js +160 -0
  870. package/dist/forms/emoji-picker/emoji-picker.d.ts +16 -0
  871. package/dist/forms/emoji-picker/emoji-picker.js +124 -0
  872. package/dist/forms/emoji-picker/index.d.ts +3 -0
  873. package/dist/forms/emoji-picker/index.js +9 -0
  874. package/dist/forms/emoji-picker/model.d.ts +8 -0
  875. package/dist/forms/emoji-picker/model.js +44 -0
  876. package/dist/forms/emoji-reaction-button/emoji-reaction-button.d.ts +28 -0
  877. package/dist/forms/emoji-reaction-button/emoji-reaction-button.js +95 -0
  878. package/dist/forms/emoji-reaction-button/index.d.ts +1 -0
  879. package/dist/forms/emoji-reaction-button/index.js +8 -0
  880. package/dist/forms/field/field.d.ts +16 -0
  881. package/dist/forms/field/field.js +26 -0
  882. package/dist/forms/field/index.d.ts +1 -0
  883. package/dist/forms/field/index.js +4 -0
  884. package/dist/forms/form-grid/form-grid.d.ts +7 -0
  885. package/dist/forms/form-grid/form-grid.js +17 -0
  886. package/dist/forms/form-grid/index.d.ts +1 -0
  887. package/dist/forms/form-grid/index.js +4 -0
  888. package/dist/forms/index.d.ts +20 -0
  889. package/dist/forms/index.js +20 -0
  890. package/dist/forms/inline-select/index.d.ts +1 -0
  891. package/dist/forms/inline-select/index.js +6 -0
  892. package/dist/forms/inline-select/inline-select.d.ts +14 -0
  893. package/dist/forms/inline-select/inline-select.js +28 -0
  894. package/dist/forms/input/index.d.ts +1 -0
  895. package/dist/forms/input/index.js +4 -0
  896. package/dist/forms/input/input.d.ts +4 -0
  897. package/dist/forms/input/input.js +17 -0
  898. package/dist/forms/input-icon/index.d.ts +1 -0
  899. package/dist/forms/input-icon/index.js +4 -0
  900. package/dist/forms/input-icon/input-icon.d.ts +7 -0
  901. package/dist/forms/input-icon/input-icon.js +19 -0
  902. package/dist/forms/internal/button-option.d.ts +18 -0
  903. package/dist/forms/internal/button-option.js +29 -0
  904. package/dist/forms/label/index.d.ts +1 -0
  905. package/dist/forms/label/index.js +4 -0
  906. package/dist/forms/label/label.d.ts +4 -0
  907. package/dist/forms/label/label.js +11 -0
  908. package/dist/forms/native-select/index.d.ts +1 -0
  909. package/dist/forms/native-select/index.js +6 -0
  910. package/dist/forms/native-select/native-select.d.ts +23 -0
  911. package/dist/forms/native-select/native-select.js +52 -0
  912. package/dist/forms/number-stepper/index.d.ts +1 -0
  913. package/dist/forms/number-stepper/index.js +4 -0
  914. package/dist/forms/number-stepper/number-stepper.d.ts +17 -0
  915. package/dist/forms/number-stepper/number-stepper.js +108 -0
  916. package/dist/forms/pin-input/index.d.ts +1 -0
  917. package/dist/forms/pin-input/index.js +4 -0
  918. package/dist/forms/pin-input/pin-input.d.ts +16 -0
  919. package/dist/forms/pin-input/pin-input.js +61 -0
  920. package/dist/forms/radio-button-group/index.d.ts +1 -0
  921. package/dist/forms/radio-button-group/index.js +6 -0
  922. package/dist/forms/radio-button-group/radio-button-group.d.ts +19 -0
  923. package/dist/forms/radio-button-group/radio-button-group.js +52 -0
  924. package/dist/forms/rating/index.d.ts +2 -0
  925. package/dist/forms/rating/index.js +9 -0
  926. package/dist/forms/rating/model.d.ts +8 -0
  927. package/dist/forms/rating/model.js +37 -0
  928. package/dist/forms/rating/rating.d.ts +20 -0
  929. package/dist/forms/rating/rating.js +114 -0
  930. package/dist/forms/select/index.d.ts +1 -0
  931. package/dist/forms/select/index.js +4 -0
  932. package/dist/forms/select/select.d.ts +8 -0
  933. package/dist/forms/select/select.js +28 -0
  934. package/dist/forms/slider/index.d.ts +2 -0
  935. package/dist/forms/slider/index.js +22 -0
  936. package/dist/forms/slider/model.d.ts +16 -0
  937. package/dist/forms/slider/model.js +65 -0
  938. package/dist/forms/slider/slider.d.ts +43 -0
  939. package/dist/forms/slider/slider.js +180 -0
  940. package/dist/forms/textarea/index.d.ts +1 -0
  941. package/dist/forms/textarea/index.js +4 -0
  942. package/dist/forms/textarea/textarea.d.ts +4 -0
  943. package/dist/forms/textarea/textarea.js +17 -0
  944. package/dist/forms/time-input/index.d.ts +1 -0
  945. package/dist/forms/time-input/index.js +4 -0
  946. package/dist/forms/time-input/time-input.d.ts +15 -0
  947. package/dist/forms/time-input/time-input.js +87 -0
  948. package/dist/navigation/floating-toc/floating-toc.d.ts +21 -0
  949. package/dist/navigation/floating-toc/floating-toc.js +86 -0
  950. package/dist/navigation/floating-toc/index.d.ts +2 -0
  951. package/dist/navigation/floating-toc/index.js +6 -0
  952. package/dist/navigation/floating-toc/model.d.ts +13 -0
  953. package/dist/navigation/floating-toc/model.js +9 -0
  954. package/dist/navigation/index.d.ts +4 -0
  955. package/dist/navigation/index.js +4 -0
  956. package/dist/navigation/segmented/index.d.ts +1 -0
  957. package/dist/navigation/segmented/index.js +8 -0
  958. package/dist/navigation/segmented/segmented.d.ts +9 -0
  959. package/dist/navigation/segmented/segmented.js +33 -0
  960. package/dist/navigation/stepper/index.d.ts +1 -0
  961. package/dist/navigation/stepper/index.js +4 -0
  962. package/dist/navigation/stepper/stepper.d.ts +11 -0
  963. package/dist/navigation/stepper/stepper.js +58 -0
  964. package/dist/navigation/tabs/index.d.ts +1 -0
  965. package/dist/navigation/tabs/index.js +12 -0
  966. package/dist/navigation/tabs/tabs.d.ts +20 -0
  967. package/dist/navigation/tabs/tabs.js +44 -0
  968. package/dist/overlays/context-menu/context-menu.d.ts +40 -0
  969. package/dist/overlays/context-menu/context-menu.js +201 -0
  970. package/dist/overlays/context-menu/index.d.ts +1 -0
  971. package/dist/overlays/context-menu/index.js +20 -0
  972. package/dist/overlays/dialog/dialog.d.ts +27 -0
  973. package/dist/overlays/dialog/dialog.js +128 -0
  974. package/dist/overlays/dialog/index.d.ts +1 -0
  975. package/dist/overlays/dialog/index.js +20 -0
  976. package/dist/overlays/drawer/drawer.d.ts +18 -0
  977. package/dist/overlays/drawer/drawer.js +82 -0
  978. package/dist/overlays/drawer/index.d.ts +1 -0
  979. package/dist/overlays/drawer/index.js +14 -0
  980. package/dist/overlays/dropdown-menu/dropdown-menu.d.ts +19 -0
  981. package/dist/overlays/dropdown-menu/dropdown-menu.js +72 -0
  982. package/dist/overlays/dropdown-menu/index.d.ts +1 -0
  983. package/dist/overlays/dropdown-menu/index.js +20 -0
  984. package/dist/overlays/focus-return/focus-return.d.ts +30 -0
  985. package/dist/overlays/focus-return/focus-return.js +36 -0
  986. package/dist/overlays/focus-return/index.d.ts +1 -0
  987. package/dist/overlays/focus-return/index.js +12 -0
  988. package/dist/overlays/hover-card/hover-card.d.ts +28 -0
  989. package/dist/overlays/hover-card/hover-card.js +133 -0
  990. package/dist/overlays/hover-card/index.d.ts +1 -0
  991. package/dist/overlays/hover-card/index.js +10 -0
  992. package/dist/overlays/index.d.ts +8 -0
  993. package/dist/overlays/index.js +8 -0
  994. package/dist/overlays/popover/index.d.ts +1 -0
  995. package/dist/overlays/popover/index.js +14 -0
  996. package/dist/overlays/popover/popover.d.ts +10 -0
  997. package/dist/overlays/popover/popover.js +34 -0
  998. package/dist/overlays/toaster/index.d.ts +1 -0
  999. package/dist/overlays/toaster/index.js +5 -0
  1000. package/dist/overlays/toaster/toaster.d.ts +10 -0
  1001. package/dist/overlays/toaster/toaster.js +30 -0
  1002. package/dist/palette/command-palette/command-palette.d.ts +32 -0
  1003. package/dist/palette/command-palette/command-palette.js +95 -0
  1004. package/dist/palette/command-palette/index.d.ts +1 -0
  1005. package/dist/palette/command-palette/index.js +6 -0
  1006. package/dist/palette/index.d.ts +1 -0
  1007. package/dist/palette/index.js +1 -0
  1008. package/dist/rich-text/index.d.ts +2 -0
  1009. package/dist/rich-text/index.js +14 -0
  1010. package/dist/rich-text/rich-text-editor.d.ts +31 -0
  1011. package/dist/rich-text/rich-text-editor.js +364 -0
  1012. package/dist/rich-text/sanitize.d.ts +17 -0
  1013. package/dist/rich-text/sanitize.js +215 -0
  1014. package/dist/shell/app-shell/app-shell.d.ts +24 -0
  1015. package/dist/shell/app-shell/app-shell.js +42 -0
  1016. package/dist/shell/app-shell/index.d.ts +1 -0
  1017. package/dist/shell/app-shell/index.js +10 -0
  1018. package/dist/shell/index.d.ts +3 -0
  1019. package/dist/shell/index.js +3 -0
  1020. package/dist/shell/sidebar/index.d.ts +1 -0
  1021. package/dist/shell/sidebar/index.js +6 -0
  1022. package/dist/shell/sidebar/sidebar.d.ts +48 -0
  1023. package/dist/shell/sidebar/sidebar.js +110 -0
  1024. package/dist/shell/topbar/index.d.ts +1 -0
  1025. package/dist/shell/topbar/index.js +4 -0
  1026. package/dist/shell/topbar/topbar.d.ts +22 -0
  1027. package/dist/shell/topbar/topbar.js +93 -0
  1028. package/dist/showcase/analytics-bento.d.ts +2 -0
  1029. package/dist/showcase/analytics-bento.js +336 -0
  1030. package/dist/showcase/bento.d.ts +43 -0
  1031. package/dist/showcase/bento.js +131 -0
  1032. package/dist/showcase/controls-bento.d.ts +2 -0
  1033. package/dist/showcase/controls-bento.js +264 -0
  1034. package/dist/showcase/dataviz-bento.d.ts +2 -0
  1035. package/dist/showcase/dataviz-bento.js +444 -0
  1036. package/dist/showcase/kit-tiles.d.ts +15 -0
  1037. package/dist/showcase/kit-tiles.js +192 -0
  1038. package/dist/showcase/operations-bento.d.ts +2 -0
  1039. package/dist/showcase/operations-bento.js +450 -0
  1040. package/dist/showcase/shell.d.ts +8 -0
  1041. package/dist/showcase/shell.js +115 -0
  1042. package/dist/states/empty-state/empty-state.d.ts +11 -0
  1043. package/dist/states/empty-state/empty-state.js +22 -0
  1044. package/dist/states/empty-state/index.d.ts +1 -0
  1045. package/dist/states/empty-state/index.js +1 -0
  1046. package/dist/states/error-state/error-state.d.ts +12 -0
  1047. package/dist/states/error-state/error-state.js +42 -0
  1048. package/dist/states/error-state/index.d.ts +1 -0
  1049. package/dist/states/error-state/index.js +1 -0
  1050. package/dist/states/forbidden-state/forbidden-state.d.ts +8 -0
  1051. package/dist/states/forbidden-state/forbidden-state.js +21 -0
  1052. package/dist/states/forbidden-state/index.d.ts +1 -0
  1053. package/dist/states/forbidden-state/index.js +1 -0
  1054. package/dist/states/index.d.ts +5 -0
  1055. package/dist/states/index.js +5 -0
  1056. package/dist/states/offline-banner/index.d.ts +1 -0
  1057. package/dist/states/offline-banner/index.js +1 -0
  1058. package/dist/states/offline-banner/offline-banner.d.ts +10 -0
  1059. package/dist/states/offline-banner/offline-banner.js +24 -0
  1060. package/dist/states/skeleton/index.d.ts +1 -0
  1061. package/dist/states/skeleton/index.js +1 -0
  1062. package/dist/states/skeleton/skeleton.d.ts +12 -0
  1063. package/dist/states/skeleton/skeleton.js +35 -0
  1064. package/dist/utils/cn.d.ts +3 -0
  1065. package/dist/utils/cn.js +86 -0
  1066. package/dist/utils/index.d.ts +2 -0
  1067. package/dist/utils/index.js +6 -0
  1068. package/dist/utils/reduced-motion.d.ts +2 -0
  1069. package/dist/utils/reduced-motion.js +18 -0
  1070. package/dist/xlsx/crc32.d.ts +4 -0
  1071. package/dist/xlsx/crc32.js +19 -0
  1072. package/dist/xlsx/index.d.ts +3 -0
  1073. package/dist/xlsx/index.js +21 -0
  1074. package/dist/xlsx/xlsx.d.ts +42 -0
  1075. package/dist/xlsx/xlsx.js +242 -0
  1076. package/dist/xlsx/zip.d.ts +16 -0
  1077. package/dist/xlsx/zip.js +98 -0
  1078. package/package.json +115 -3
  1079. package/scripts/build-theme.mjs +423 -0
  1080. package/src/ai/attachment/attachment.tsx +111 -0
  1081. package/src/ai/attachment/index.ts +1 -0
  1082. package/src/ai/chain-of-thought/chain-of-thought.tsx +120 -0
  1083. package/src/ai/chain-of-thought/index.ts +1 -0
  1084. package/src/ai/chat-list/chat-list.tsx +124 -0
  1085. package/src/ai/chat-list/group.ts +48 -0
  1086. package/src/ai/chat-list/index.ts +2 -0
  1087. package/src/ai/chat-message/chat-message.tsx +116 -0
  1088. package/src/ai/chat-message/index.ts +1 -0
  1089. package/src/ai/code-block/code-block.tsx +91 -0
  1090. package/src/ai/code-block/highlight.ts +195 -0
  1091. package/src/ai/code-block/index.ts +8 -0
  1092. package/src/ai/conversation/conversation.tsx +97 -0
  1093. package/src/ai/conversation/index.ts +9 -0
  1094. package/src/ai/conversation/stick.ts +40 -0
  1095. package/src/ai/index.ts +14 -0
  1096. package/src/ai/internal/disclosure.tsx +65 -0
  1097. package/src/ai/internal/icon-action.tsx +25 -0
  1098. package/src/ai/internal/keyframes.tsx +13 -0
  1099. package/src/ai/internal/use-copy.ts +38 -0
  1100. package/src/ai/loader/index.ts +1 -0
  1101. package/src/ai/loader/loader.tsx +60 -0
  1102. package/src/ai/markdown/index.ts +2 -0
  1103. package/src/ai/markdown/markdown.tsx +211 -0
  1104. package/src/ai/markdown/parse.ts +276 -0
  1105. package/src/ai/message-actions/index.ts +1 -0
  1106. package/src/ai/message-actions/message-actions.tsx +85 -0
  1107. package/src/ai/prompt-input/index.ts +2 -0
  1108. package/src/ai/prompt-input/keys.ts +11 -0
  1109. package/src/ai/prompt-input/prompt-input.tsx +148 -0
  1110. package/src/ai/prompt-suggestion/index.ts +1 -0
  1111. package/src/ai/prompt-suggestion/prompt-suggestion.tsx +54 -0
  1112. package/src/ai/source/index.ts +1 -0
  1113. package/src/ai/source/source.tsx +73 -0
  1114. package/src/ai/text-shimmer/index.ts +1 -0
  1115. package/src/ai/text-shimmer/text-shimmer.tsx +42 -0
  1116. package/src/ai/tool-call/index.ts +1 -0
  1117. package/src/ai/tool-call/tool-call.tsx +108 -0
  1118. package/src/atoms/avatar/avatar.tsx +51 -0
  1119. package/src/atoms/avatar/index.ts +1 -0
  1120. package/src/atoms/avatar-group/avatar-group.tsx +16 -0
  1121. package/src/atoms/avatar-group/index.ts +1 -0
  1122. package/src/atoms/big-date/big-date.tsx +23 -0
  1123. package/src/atoms/big-date/index.ts +1 -0
  1124. package/src/atoms/button/button.tsx +76 -0
  1125. package/src/atoms/button/index.ts +6 -0
  1126. package/src/atoms/checkbox/checkbox.tsx +23 -0
  1127. package/src/atoms/checkbox/index.ts +1 -0
  1128. package/src/atoms/chip/chip.tsx +41 -0
  1129. package/src/atoms/chip/index.ts +1 -0
  1130. package/src/atoms/icon/icon-map.ts +83 -0
  1131. package/src/atoms/icon/icon.tsx +23 -0
  1132. package/src/atoms/icon/index.ts +2 -0
  1133. package/src/atoms/index.ts +16 -0
  1134. package/src/atoms/led/index.ts +1 -0
  1135. package/src/atoms/led/led.tsx +30 -0
  1136. package/src/atoms/number-value/format.ts +28 -0
  1137. package/src/atoms/number-value/index.ts +8 -0
  1138. package/src/atoms/number-value/number-value.tsx +60 -0
  1139. package/src/atoms/pressable-feedback/index.ts +1 -0
  1140. package/src/atoms/pressable-feedback/pressable-feedback.tsx +79 -0
  1141. package/src/atoms/progress/index.ts +1 -0
  1142. package/src/atoms/progress/progress.tsx +32 -0
  1143. package/src/atoms/radio-group/index.ts +5 -0
  1144. package/src/atoms/radio-group/radio-group.tsx +52 -0
  1145. package/src/atoms/status-dot/index.ts +6 -0
  1146. package/src/atoms/status-dot/status-dot.tsx +52 -0
  1147. package/src/atoms/status-pill/index.ts +1 -0
  1148. package/src/atoms/status-pill/status-pill.tsx +32 -0
  1149. package/src/atoms/switch/index.ts +1 -0
  1150. package/src/atoms/switch/switch.tsx +20 -0
  1151. package/src/atoms/trend-chip/index.ts +8 -0
  1152. package/src/atoms/trend-chip/trend-chip.tsx +73 -0
  1153. package/src/atoms/trend-chip/trend.ts +24 -0
  1154. package/src/brand/export.ts +67 -0
  1155. package/src/brand/generate.ts +531 -0
  1156. package/src/brand/glyphs.ts +113 -0
  1157. package/src/brand/index.ts +8 -0
  1158. package/src/brand/initials.ts +51 -0
  1159. package/src/brand/logo-forge.tsx +523 -0
  1160. package/src/brand/logo-mark.tsx +40 -0
  1161. package/src/brand/palettes.ts +69 -0
  1162. package/src/brand/rng.ts +25 -0
  1163. package/src/calendar/day-chip/day-chip.tsx +34 -0
  1164. package/src/calendar/day-chip/index.ts +2 -0
  1165. package/src/calendar/event-block/event-block.tsx +74 -0
  1166. package/src/calendar/event-block/index.ts +2 -0
  1167. package/src/calendar/index.ts +5 -0
  1168. package/src/calendar/internal/date-utils.ts +78 -0
  1169. package/src/calendar/internal/index.ts +3 -0
  1170. package/src/calendar/internal/layout.ts +60 -0
  1171. package/src/calendar/internal/styles.ts +3 -0
  1172. package/src/calendar/internal/types.ts +32 -0
  1173. package/src/calendar/month-view/index.ts +1 -0
  1174. package/src/calendar/month-view/month-view.tsx +157 -0
  1175. package/src/calendar/timeline-view/index.ts +1 -0
  1176. package/src/calendar/timeline-view/timeline-view.tsx +100 -0
  1177. package/src/calendar/week-view/index.ts +1 -0
  1178. package/src/calendar/week-view/week-view.tsx +216 -0
  1179. package/src/charts/README.md +598 -0
  1180. package/src/charts/area-chart/area-chart.tsx +23 -0
  1181. package/src/charts/area-chart/index.ts +1 -0
  1182. package/src/charts/bar-chart/bar-chart.tsx +68 -0
  1183. package/src/charts/bar-chart/bar-race-chart.tsx +303 -0
  1184. package/src/charts/bar-chart/bar-race.ts +67 -0
  1185. package/src/charts/bar-chart/drilldown-bar-chart.tsx +85 -0
  1186. package/src/charts/bar-chart/index.ts +4 -0
  1187. package/src/charts/beeswarm-chart/beeswarm-chart.tsx +470 -0
  1188. package/src/charts/beeswarm-chart/index.ts +1 -0
  1189. package/src/charts/box-plot/box-plot.tsx +417 -0
  1190. package/src/charts/box-plot/index.ts +1 -0
  1191. package/src/charts/bubble-chart/bubble-chart.tsx +14 -0
  1192. package/src/charts/bubble-chart/index.ts +1 -0
  1193. package/src/charts/bubble-map/bubble-map.tsx +678 -0
  1194. package/src/charts/bubble-map/index.ts +1 -0
  1195. package/src/charts/bullet-chart/bullet-chart.tsx +577 -0
  1196. package/src/charts/bullet-chart/bullet-layout.ts +47 -0
  1197. package/src/charts/bullet-chart/index.ts +2 -0
  1198. package/src/charts/candlestick-chart/candlestick-chart.tsx +608 -0
  1199. package/src/charts/candlestick-chart/index.ts +1 -0
  1200. package/src/charts/chart-grid/chart-grid.tsx +197 -0
  1201. package/src/charts/chart-grid/grid-math.ts +69 -0
  1202. package/src/charts/chart-grid/index.ts +2 -0
  1203. package/src/charts/chord-chart/chord-chart.tsx +551 -0
  1204. package/src/charts/chord-chart/index.ts +1 -0
  1205. package/src/charts/choropleth-map/choropleth-drilldown.tsx +98 -0
  1206. package/src/charts/choropleth-map/choropleth-map.tsx +425 -0
  1207. package/src/charts/choropleth-map/index.ts +2 -0
  1208. package/src/charts/combo-chart/combo-chart.tsx +29 -0
  1209. package/src/charts/combo-chart/index.ts +1 -0
  1210. package/src/charts/core/a11y.ts +2 -0
  1211. package/src/charts/core/axes.tsx +176 -0
  1212. package/src/charts/core/breadcrumb.tsx +68 -0
  1213. package/src/charts/core/cartesian/annotations.tsx +161 -0
  1214. package/src/charts/core/cartesian/axis-stack.ts +88 -0
  1215. package/src/charts/core/cartesian/brush.tsx +241 -0
  1216. package/src/charts/core/cartesian/cartesian-chart.tsx +1239 -0
  1217. package/src/charts/core/cartesian/category-group-axis.tsx +43 -0
  1218. package/src/charts/core/cartesian/category-groups.ts +40 -0
  1219. package/src/charts/core/cartesian/ink-layer.tsx +463 -0
  1220. package/src/charts/core/cartesian/layout.ts +796 -0
  1221. package/src/charts/core/cartesian/marks.tsx +158 -0
  1222. package/src/charts/core/cartesian/sync-store.ts +103 -0
  1223. package/src/charts/core/cartesian/sync.tsx +68 -0
  1224. package/src/charts/core/cartesian/types.ts +169 -0
  1225. package/src/charts/core/category-axis.ts +120 -0
  1226. package/src/charts/core/category-explorer.tsx +132 -0
  1227. package/src/charts/core/chart-frame.tsx +119 -0
  1228. package/src/charts/core/chart-states.tsx +89 -0
  1229. package/src/charts/core/chord.ts +176 -0
  1230. package/src/charts/core/color-scale.ts +117 -0
  1231. package/src/charts/core/data-table.tsx +49 -0
  1232. package/src/charts/core/distribution/brush.ts +151 -0
  1233. package/src/charts/core/distribution/dot-stack.ts +78 -0
  1234. package/src/charts/core/distribution/explorer.tsx +96 -0
  1235. package/src/charts/core/distribution/geometry.ts +76 -0
  1236. package/src/charts/core/distribution/index.ts +11 -0
  1237. package/src/charts/core/distribution/labels.ts +56 -0
  1238. package/src/charts/core/distribution/layout.ts +302 -0
  1239. package/src/charts/core/distribution/marks.tsx +334 -0
  1240. package/src/charts/core/distribution/observations.tsx +61 -0
  1241. package/src/charts/core/distribution/point-index.ts +68 -0
  1242. package/src/charts/core/distribution/points.ts +35 -0
  1243. package/src/charts/core/distribution/stats.ts +426 -0
  1244. package/src/charts/core/distribution/use-legend.tsx +76 -0
  1245. package/src/charts/core/distribution/xy-points.tsx +1202 -0
  1246. package/src/charts/core/downsample.ts +85 -0
  1247. package/src/charts/core/drawing-height.ts +22 -0
  1248. package/src/charts/core/export-menu.tsx +68 -0
  1249. package/src/charts/core/export.ts +217 -0
  1250. package/src/charts/core/format.ts +154 -0
  1251. package/src/charts/core/gantt.ts +531 -0
  1252. package/src/charts/core/hierarchy-chart.tsx +236 -0
  1253. package/src/charts/core/hierarchy.ts +380 -0
  1254. package/src/charts/core/index.ts +54 -0
  1255. package/src/charts/core/indicators.ts +175 -0
  1256. package/src/charts/core/ink.ts +321 -0
  1257. package/src/charts/core/legend.tsx +111 -0
  1258. package/src/charts/core/motion.ts +112 -0
  1259. package/src/charts/core/palette.ts +76 -0
  1260. package/src/charts/core/path.ts +198 -0
  1261. package/src/charts/core/pattern-defs.tsx +87 -0
  1262. package/src/charts/core/patterns.ts +121 -0
  1263. package/src/charts/core/plot-overlay.tsx +106 -0
  1264. package/src/charts/core/polar.ts +316 -0
  1265. package/src/charts/core/sankey.ts +544 -0
  1266. package/src/charts/core/scales/band.ts +90 -0
  1267. package/src/charts/core/scales/index.ts +4 -0
  1268. package/src/charts/core/scales/linear.ts +94 -0
  1269. package/src/charts/core/scales/log.ts +35 -0
  1270. package/src/charts/core/scales/time.ts +136 -0
  1271. package/src/charts/core/shape-morph.ts +101 -0
  1272. package/src/charts/core/stack.ts +41 -0
  1273. package/src/charts/core/time-range.ts +51 -0
  1274. package/src/charts/core/tooltip.tsx +126 -0
  1275. package/src/charts/core/tree-fit.ts +90 -0
  1276. package/src/charts/core/tree-layout.ts +384 -0
  1277. package/src/charts/core/tree-radial.ts +303 -0
  1278. package/src/charts/core/types.ts +105 -0
  1279. package/src/charts/core/unit-glyph.tsx +102 -0
  1280. package/src/charts/core/unit-icons.ts +210 -0
  1281. package/src/charts/core/units.ts +267 -0
  1282. package/src/charts/core/use-canvas.ts +77 -0
  1283. package/src/charts/core/use-chart-size.ts +65 -0
  1284. package/src/charts/core/use-drill.ts +100 -0
  1285. package/src/charts/core/use-index-navigation.ts +68 -0
  1286. package/src/charts/core/use-pan-zoom.ts +219 -0
  1287. package/src/charts/core/use-series-state.ts +40 -0
  1288. package/src/charts/crossfilter/crossfilter-engine.ts +207 -0
  1289. package/src/charts/crossfilter/crossfilter.tsx +494 -0
  1290. package/src/charts/crossfilter/index.ts +2 -0
  1291. package/src/charts/donut-chart/center-fit.ts +53 -0
  1292. package/src/charts/donut-chart/donut-chart.tsx +826 -0
  1293. package/src/charts/donut-chart/index.ts +1 -0
  1294. package/src/charts/dumbbell-chart/dumbbell-chart.tsx +723 -0
  1295. package/src/charts/dumbbell-chart/index.ts +1 -0
  1296. package/src/charts/fill-gauge-chart/fill-gauge-chart.tsx +319 -0
  1297. package/src/charts/fill-gauge-chart/index.ts +1 -0
  1298. package/src/charts/funnel-chart/funnel-chart.tsx +517 -0
  1299. package/src/charts/funnel-chart/index.ts +1 -0
  1300. package/src/charts/gantt-chart/gantt-chart.tsx +1369 -0
  1301. package/src/charts/gantt-chart/gantt-export.ts +139 -0
  1302. package/src/charts/gantt-chart/gantt-marks.tsx +710 -0
  1303. package/src/charts/gantt-chart/index.ts +1 -0
  1304. package/src/charts/gauge-chart/gauge-chart.tsx +594 -0
  1305. package/src/charts/gauge-chart/index.ts +1 -0
  1306. package/src/charts/globe-chart/globe-chart.tsx +744 -0
  1307. package/src/charts/globe-chart/index.ts +1 -0
  1308. package/src/charts/heatmap-chart/calendar-heatmap.tsx +221 -0
  1309. package/src/charts/heatmap-chart/calendar-layout.ts +68 -0
  1310. package/src/charts/heatmap-chart/datetime-columns.ts +38 -0
  1311. package/src/charts/heatmap-chart/drilldown-heatmap.tsx +92 -0
  1312. package/src/charts/heatmap-chart/heatmap-chart.tsx +189 -0
  1313. package/src/charts/heatmap-chart/heatmap-grid.tsx +588 -0
  1314. package/src/charts/heatmap-chart/heatmap-scale.ts +64 -0
  1315. package/src/charts/heatmap-chart/hex-layout.ts +88 -0
  1316. package/src/charts/heatmap-chart/index.ts +9 -0
  1317. package/src/charts/hex-tile-map/hex-layout.ts +147 -0
  1318. package/src/charts/hex-tile-map/hex-tile-map.tsx +361 -0
  1319. package/src/charts/hex-tile-map/index.ts +2 -0
  1320. package/src/charts/hierarchy-morph/hierarchy-morph.tsx +455 -0
  1321. package/src/charts/hierarchy-morph/index.ts +1 -0
  1322. package/src/charts/histogram/histogram.tsx +556 -0
  1323. package/src/charts/histogram/index.ts +1 -0
  1324. package/src/charts/icicle-chart/icicle-chart.tsx +466 -0
  1325. package/src/charts/icicle-chart/index.ts +1 -0
  1326. package/src/charts/index.ts +50 -0
  1327. package/src/charts/isotype-rows/index.ts +1 -0
  1328. package/src/charts/isotype-rows/isotype-rows.tsx +336 -0
  1329. package/src/charts/line-chart/index.ts +2 -0
  1330. package/src/charts/line-chart/line-chart.tsx +36 -0
  1331. package/src/charts/line-chart/line-drilldown.tsx +65 -0
  1332. package/src/charts/lollipop-chart/index.ts +1 -0
  1333. package/src/charts/lollipop-chart/lollipop-chart.tsx +402 -0
  1334. package/src/charts/maps/data/README.md +51 -0
  1335. package/src/charts/maps/data/peru/index.ts +5 -0
  1336. package/src/charts/maps/data/world/index.ts +5 -0
  1337. package/src/charts/maps/geo/build-topology.ts +226 -0
  1338. package/src/charts/maps/geo/cluster.ts +81 -0
  1339. package/src/charts/maps/geo/geo-path.ts +153 -0
  1340. package/src/charts/maps/geo/globe.ts +309 -0
  1341. package/src/charts/maps/geo/index.ts +7 -0
  1342. package/src/charts/maps/geo/map-data-table.tsx +141 -0
  1343. package/src/charts/maps/geo/map-labels.ts +60 -0
  1344. package/src/charts/maps/geo/map-legend.tsx +82 -0
  1345. package/src/charts/maps/geo/map-viewport.tsx +404 -0
  1346. package/src/charts/maps/geo/match.ts +12 -0
  1347. package/src/charts/maps/geo/projection.ts +177 -0
  1348. package/src/charts/maps/geo/pure.ts +12 -0
  1349. package/src/charts/maps/geo/routes.ts +95 -0
  1350. package/src/charts/maps/geo/simplify.ts +57 -0
  1351. package/src/charts/maps/geo/topojson.ts +94 -0
  1352. package/src/charts/maps/geo/types.ts +59 -0
  1353. package/src/charts/maps/geo/use-map-geometry.ts +132 -0
  1354. package/src/charts/maps/geo/use-map-view.ts +253 -0
  1355. package/src/charts/maps/geo/view.ts +100 -0
  1356. package/src/charts/maps/index.ts +1 -0
  1357. package/src/charts/parliament-chart/index.ts +1 -0
  1358. package/src/charts/parliament-chart/parliament-chart.tsx +406 -0
  1359. package/src/charts/pictogram-chart/index.ts +1 -0
  1360. package/src/charts/pictogram-chart/pictogram-chart.tsx +406 -0
  1361. package/src/charts/pie-chart/index.ts +1 -0
  1362. package/src/charts/pie-chart/pie-chart.tsx +16 -0
  1363. package/src/charts/polar-area-chart/index.ts +1 -0
  1364. package/src/charts/polar-area-chart/polar-area-chart.tsx +505 -0
  1365. package/src/charts/progress-units/index.ts +1 -0
  1366. package/src/charts/progress-units/progress-units.tsx +209 -0
  1367. package/src/charts/radar-chart/index.ts +1 -0
  1368. package/src/charts/radar-chart/radar-chart.tsx +550 -0
  1369. package/src/charts/radial-bar-chart/index.ts +1 -0
  1370. package/src/charts/radial-bar-chart/radial-bar-chart.tsx +552 -0
  1371. package/src/charts/raincloud-chart/index.ts +1 -0
  1372. package/src/charts/raincloud-chart/raincloud-chart.tsx +453 -0
  1373. package/src/charts/range-area-chart/index.ts +1 -0
  1374. package/src/charts/range-area-chart/range-area-chart.tsx +516 -0
  1375. package/src/charts/sankey-chart/alluvial-chart.tsx +56 -0
  1376. package/src/charts/sankey-chart/index.ts +2 -0
  1377. package/src/charts/sankey-chart/sankey-chart.tsx +1139 -0
  1378. package/src/charts/scatter-chart/index.ts +1 -0
  1379. package/src/charts/scatter-chart/scatter-chart.tsx +16 -0
  1380. package/src/charts/slope-chart/index.ts +2 -0
  1381. package/src/charts/slope-chart/label-layout.ts +48 -0
  1382. package/src/charts/slope-chart/slope-chart.tsx +484 -0
  1383. package/src/charts/sparkline/index.ts +1 -0
  1384. package/src/charts/sparkline/sparkline.tsx +266 -0
  1385. package/src/charts/stock-chart/index.ts +1 -0
  1386. package/src/charts/stock-chart/stock-chart.tsx +728 -0
  1387. package/src/charts/stock-chart/stock-drawing-layer.tsx +386 -0
  1388. package/src/charts/stock-chart/stock-drawings.ts +121 -0
  1389. package/src/charts/stock-chart/stock-model.ts +167 -0
  1390. package/src/charts/stock-chart/stock-plot.tsx +1133 -0
  1391. package/src/charts/stock-chart/stock-stats.ts +122 -0
  1392. package/src/charts/stock-chart/stock-transforms.ts +86 -0
  1393. package/src/charts/stock-chart/use-stock-stream.ts +28 -0
  1394. package/src/charts/stream-graph/index.ts +2 -0
  1395. package/src/charts/stream-graph/stream-graph.tsx +521 -0
  1396. package/src/charts/stream-graph/stream-layout.ts +131 -0
  1397. package/src/charts/sunburst-chart/index.ts +1 -0
  1398. package/src/charts/sunburst-chart/sunburst-chart.tsx +558 -0
  1399. package/src/charts/timeline-chart/index.ts +2 -0
  1400. package/src/charts/timeline-chart/timeline-chart.tsx +656 -0
  1401. package/src/charts/timeline-chart/timeline-lanes.ts +116 -0
  1402. package/src/charts/tree-chart/index.ts +3 -0
  1403. package/src/charts/tree-chart/tree-card.tsx +132 -0
  1404. package/src/charts/tree-chart/tree-chart.tsx +1071 -0
  1405. package/src/charts/tree-chart/tree-model.ts +217 -0
  1406. package/src/charts/tree-chart/use-tree-transition.ts +116 -0
  1407. package/src/charts/treemap-chart/index.ts +1 -0
  1408. package/src/charts/treemap-chart/treemap-chart.tsx +435 -0
  1409. package/src/charts/violin-chart/index.ts +1 -0
  1410. package/src/charts/violin-chart/violin-chart.tsx +514 -0
  1411. package/src/charts/waffle-chart/index.ts +1 -0
  1412. package/src/charts/waffle-chart/waffle-chart.tsx +354 -0
  1413. package/src/charts/waterfall-chart/index.ts +2 -0
  1414. package/src/charts/waterfall-chart/waterfall-chart.tsx +629 -0
  1415. package/src/charts/waterfall-chart/waterfall-layout.ts +112 -0
  1416. package/src/containers/action-bar/action-bar.tsx +78 -0
  1417. package/src/containers/action-bar/index.ts +1 -0
  1418. package/src/containers/banner/banner.tsx +68 -0
  1419. package/src/containers/banner/index.ts +1 -0
  1420. package/src/containers/card/card.tsx +44 -0
  1421. package/src/containers/card/index.ts +1 -0
  1422. package/src/containers/carousel/carousel.tsx +216 -0
  1423. package/src/containers/carousel/index.ts +2 -0
  1424. package/src/containers/carousel/model.ts +21 -0
  1425. package/src/containers/cell/cell.tsx +127 -0
  1426. package/src/containers/cell/index.ts +8 -0
  1427. package/src/containers/cols/cols.tsx +64 -0
  1428. package/src/containers/cols/index.ts +1 -0
  1429. package/src/containers/comment/comment.tsx +56 -0
  1430. package/src/containers/comment/index.ts +1 -0
  1431. package/src/containers/data-grid/README.md +38 -0
  1432. package/src/containers/data-grid/chart-panel.tsx +141 -0
  1433. package/src/containers/data-grid/chart-range.ts +131 -0
  1434. package/src/containers/data-grid/column-groups.ts +45 -0
  1435. package/src/containers/data-grid/column-menu.tsx +444 -0
  1436. package/src/containers/data-grid/data-grid.demos.tsx +577 -0
  1437. package/src/containers/data-grid/data-grid.fixtures.tsx +358 -0
  1438. package/src/containers/data-grid/data-grid.samples.tsx +441 -0
  1439. package/src/containers/data-grid/data-grid.tsx +3419 -0
  1440. package/src/containers/data-grid/data-source.ts +144 -0
  1441. package/src/containers/data-grid/fill-series.ts +282 -0
  1442. package/src/containers/data-grid/filter-builder-editor.tsx +262 -0
  1443. package/src/containers/data-grid/filter-builder.ts +308 -0
  1444. package/src/containers/data-grid/formula-bar.tsx +80 -0
  1445. package/src/containers/data-grid/formula-sheet.ts +254 -0
  1446. package/src/containers/data-grid/formula.ts +687 -0
  1447. package/src/containers/data-grid/grid-edit.ts +137 -0
  1448. package/src/containers/data-grid/grid-toolbar.tsx +307 -0
  1449. package/src/containers/data-grid/history.ts +137 -0
  1450. package/src/containers/data-grid/index.ts +18 -0
  1451. package/src/containers/data-grid/labels.ts +315 -0
  1452. package/src/containers/data-grid/model.ts +641 -0
  1453. package/src/containers/data-grid/pivot.ts +188 -0
  1454. package/src/containers/data-grid/range.ts +124 -0
  1455. package/src/containers/data-grid/rows.ts +95 -0
  1456. package/src/containers/data-grid/spreadsheet.demos.tsx +520 -0
  1457. package/src/containers/data-grid/tool-panel.tsx +289 -0
  1458. package/src/containers/data-grid/tree.ts +97 -0
  1459. package/src/containers/data-grid/types.ts +166 -0
  1460. package/src/containers/data-grid/use-data-source.ts +124 -0
  1461. package/src/containers/data-grid/use-grid-layout.ts +133 -0
  1462. package/src/containers/data-grid/virtual.ts +158 -0
  1463. package/src/containers/data-grid/xlsx-export.ts +54 -0
  1464. package/src/containers/description-list/description-list.tsx +25 -0
  1465. package/src/containers/description-list/index.ts +1 -0
  1466. package/src/containers/facts/facts.tsx +26 -0
  1467. package/src/containers/facts/index.ts +1 -0
  1468. package/src/containers/file-tree/file-tree.tsx +185 -0
  1469. package/src/containers/file-tree/index.ts +12 -0
  1470. package/src/containers/file-tree/model.ts +111 -0
  1471. package/src/containers/generic-row/generic-row.tsx +41 -0
  1472. package/src/containers/generic-row/index.ts +1 -0
  1473. package/src/containers/hbars/hbars.tsx +204 -0
  1474. package/src/containers/hbars/index.ts +1 -0
  1475. package/src/containers/highlight-line/highlight-line.tsx +17 -0
  1476. package/src/containers/highlight-line/index.ts +1 -0
  1477. package/src/containers/index.ts +26 -0
  1478. package/src/containers/item-card-group/index.ts +5 -0
  1479. package/src/containers/item-card-group/item-card-group.tsx +106 -0
  1480. package/src/containers/kanban/index.ts +14 -0
  1481. package/src/containers/kanban/kanban.tsx +402 -0
  1482. package/src/containers/kanban/model.ts +141 -0
  1483. package/src/containers/kpi/index.ts +1 -0
  1484. package/src/containers/kpi/kpi.tsx +53 -0
  1485. package/src/containers/kpi-group/index.ts +1 -0
  1486. package/src/containers/kpi-group/kpi-group.tsx +71 -0
  1487. package/src/containers/page-header/index.ts +1 -0
  1488. package/src/containers/page-header/page-header.tsx +53 -0
  1489. package/src/containers/resizable/index.ts +9 -0
  1490. package/src/containers/resizable/model.ts +94 -0
  1491. package/src/containers/resizable/resizable.tsx +281 -0
  1492. package/src/containers/scrolly/index.ts +8 -0
  1493. package/src/containers/scrolly/scrolly-logic.ts +41 -0
  1494. package/src/containers/scrolly/scrolly.tsx +206 -0
  1495. package/src/containers/settings-layout/index.ts +1 -0
  1496. package/src/containers/settings-layout/settings-layout.tsx +85 -0
  1497. package/src/containers/settings-row/index.ts +1 -0
  1498. package/src/containers/settings-row/settings-row.tsx +38 -0
  1499. package/src/containers/table/index.ts +1 -0
  1500. package/src/containers/table/pin.ts +91 -0
  1501. package/src/containers/table/table.tsx +406 -0
  1502. package/src/containers/timeline/index.ts +6 -0
  1503. package/src/containers/timeline/timeline.tsx +93 -0
  1504. package/src/containers/toolbar/index.ts +1 -0
  1505. package/src/containers/toolbar/toolbar.tsx +21 -0
  1506. package/src/dashboards/data.ts +40 -0
  1507. package/src/dashboards/inicio-dashboard.tsx +209 -0
  1508. package/src/dashboards/layout.tsx +95 -0
  1509. package/src/forms/accent-picker/accent-picker.tsx +68 -0
  1510. package/src/forms/accent-picker/index.ts +5 -0
  1511. package/src/forms/checkbox-button-group/checkbox-button-group.tsx +66 -0
  1512. package/src/forms/checkbox-button-group/index.ts +5 -0
  1513. package/src/forms/dropzone/dropzone.tsx +127 -0
  1514. package/src/forms/dropzone/index.ts +6 -0
  1515. package/src/forms/emoji-picker/emoji-data.ts +173 -0
  1516. package/src/forms/emoji-picker/emoji-picker.tsx +142 -0
  1517. package/src/forms/emoji-picker/index.ts +3 -0
  1518. package/src/forms/emoji-picker/model.ts +49 -0
  1519. package/src/forms/emoji-reaction-button/emoji-reaction-button.tsx +112 -0
  1520. package/src/forms/emoji-reaction-button/index.ts +7 -0
  1521. package/src/forms/field/field.tsx +53 -0
  1522. package/src/forms/field/index.ts +1 -0
  1523. package/src/forms/form-grid/form-grid.tsx +22 -0
  1524. package/src/forms/form-grid/index.ts +1 -0
  1525. package/src/forms/index.ts +20 -0
  1526. package/src/forms/inline-select/index.ts +5 -0
  1527. package/src/forms/inline-select/inline-select.tsx +41 -0
  1528. package/src/forms/input/index.ts +1 -0
  1529. package/src/forms/input/input.tsx +16 -0
  1530. package/src/forms/input-icon/index.ts +1 -0
  1531. package/src/forms/input-icon/input-icon.tsx +22 -0
  1532. package/src/forms/internal/button-option.tsx +46 -0
  1533. package/src/forms/label/index.ts +1 -0
  1534. package/src/forms/label/label.tsx +11 -0
  1535. package/src/forms/native-select/index.ts +6 -0
  1536. package/src/forms/native-select/native-select.tsx +87 -0
  1537. package/src/forms/number-stepper/index.ts +1 -0
  1538. package/src/forms/number-stepper/number-stepper.tsx +124 -0
  1539. package/src/forms/pin-input/index.ts +1 -0
  1540. package/src/forms/pin-input/pin-input.tsx +79 -0
  1541. package/src/forms/radio-button-group/index.ts +5 -0
  1542. package/src/forms/radio-button-group/radio-button-group.tsx +68 -0
  1543. package/src/forms/rating/index.ts +2 -0
  1544. package/src/forms/rating/model.ts +48 -0
  1545. package/src/forms/rating/rating.tsx +139 -0
  1546. package/src/forms/select/index.ts +1 -0
  1547. package/src/forms/select/select.tsx +29 -0
  1548. package/src/forms/slider/index.ts +17 -0
  1549. package/src/forms/slider/model.ts +89 -0
  1550. package/src/forms/slider/slider.tsx +241 -0
  1551. package/src/forms/textarea/index.ts +1 -0
  1552. package/src/forms/textarea/textarea.tsx +16 -0
  1553. package/src/forms/time-input/index.ts +1 -0
  1554. package/src/forms/time-input/time-input.tsx +116 -0
  1555. package/src/foundations/presets-gallery.mdx +59 -0
  1556. package/src/navigation/floating-toc/floating-toc.tsx +115 -0
  1557. package/src/navigation/floating-toc/index.ts +2 -0
  1558. package/src/navigation/floating-toc/model.ts +23 -0
  1559. package/src/navigation/index.ts +4 -0
  1560. package/src/navigation/segmented/index.ts +6 -0
  1561. package/src/navigation/segmented/segmented.tsx +36 -0
  1562. package/src/navigation/stepper/index.ts +1 -0
  1563. package/src/navigation/stepper/stepper.tsx +60 -0
  1564. package/src/navigation/tabs/index.ts +10 -0
  1565. package/src/navigation/tabs/tabs.tsx +55 -0
  1566. package/src/overlays/context-menu/context-menu.tsx +256 -0
  1567. package/src/overlays/context-menu/index.ts +15 -0
  1568. package/src/overlays/dialog/dialog.tsx +146 -0
  1569. package/src/overlays/dialog/index.ts +12 -0
  1570. package/src/overlays/drawer/drawer.tsx +94 -0
  1571. package/src/overlays/drawer/index.ts +8 -0
  1572. package/src/overlays/dropdown-menu/dropdown-menu.tsx +82 -0
  1573. package/src/overlays/dropdown-menu/index.ts +14 -0
  1574. package/src/overlays/focus-return/focus-return.tsx +60 -0
  1575. package/src/overlays/focus-return/index.ts +7 -0
  1576. package/src/overlays/hover-card/hover-card.tsx +186 -0
  1577. package/src/overlays/hover-card/index.ts +8 -0
  1578. package/src/overlays/index.ts +8 -0
  1579. package/src/overlays/popover/index.ts +8 -0
  1580. package/src/overlays/popover/popover.tsx +35 -0
  1581. package/src/overlays/toaster/index.ts +1 -0
  1582. package/src/overlays/toaster/toaster.tsx +35 -0
  1583. package/src/palette/command-palette/command-palette.tsx +131 -0
  1584. package/src/palette/command-palette/index.ts +5 -0
  1585. package/src/palette/index.ts +1 -0
  1586. package/src/rich-text/index.ts +8 -0
  1587. package/src/rich-text/rich-text-editor.tsx +419 -0
  1588. package/src/rich-text/sanitize.ts +266 -0
  1589. package/src/shell/app-shell/app-shell.tsx +76 -0
  1590. package/src/shell/app-shell/index.ts +6 -0
  1591. package/src/shell/index.ts +3 -0
  1592. package/src/shell/sidebar/index.ts +8 -0
  1593. package/src/shell/sidebar/sidebar.tsx +172 -0
  1594. package/src/shell/topbar/index.ts +1 -0
  1595. package/src/shell/topbar/topbar.tsx +120 -0
  1596. package/src/showcase/analytics-bento.tsx +333 -0
  1597. package/src/showcase/bento.tsx +169 -0
  1598. package/src/showcase/controls-bento.tsx +313 -0
  1599. package/src/showcase/dataviz-bento.tsx +452 -0
  1600. package/src/showcase/kit-tiles.tsx +193 -0
  1601. package/src/showcase/operations-bento.tsx +478 -0
  1602. package/src/showcase/shell.tsx +136 -0
  1603. package/src/states/empty-state/empty-state.tsx +40 -0
  1604. package/src/states/empty-state/index.ts +1 -0
  1605. package/src/states/error-state/error-state.tsx +52 -0
  1606. package/src/states/error-state/index.ts +1 -0
  1607. package/src/states/forbidden-state/forbidden-state.tsx +28 -0
  1608. package/src/states/forbidden-state/index.ts +1 -0
  1609. package/src/states/index.ts +5 -0
  1610. package/src/states/offline-banner/index.ts +1 -0
  1611. package/src/states/offline-banner/offline-banner.tsx +35 -0
  1612. package/src/states/skeleton/index.ts +1 -0
  1613. package/src/states/skeleton/skeleton.tsx +53 -0
  1614. package/src/utils/cn.ts +94 -0
  1615. package/src/utils/index.ts +2 -0
  1616. package/src/utils/reduced-motion.ts +20 -0
  1617. package/src/xlsx/crc32.ts +22 -0
  1618. package/src/xlsx/index.ts +15 -0
  1619. package/src/xlsx/xlsx.ts +390 -0
  1620. package/src/xlsx/zip.ts +117 -0
  1621. package/styles/Theme.kt +246 -0
  1622. package/styles/base.css +45 -0
  1623. package/styles/fonts.css +2 -0
  1624. package/styles/theme.css +272 -0
  1625. package/styles/tokens.css +1109 -0
  1626. package/tokens/default.json +1802 -0
@@ -0,0 +1,3419 @@
1
+ // biome-ignore-all lint/a11y/noNoninteractiveElementToInteractiveRole: an ARIA grid is a table with role="grid"; its cells are gridcells.
2
+ // biome-ignore-all lint/a11y/noInteractiveElementToNoninteractiveRole: spacer and filler cells of the grid are presentational.
3
+ // biome-ignore-all lint/a11y/noAriaHiddenOnFocusable: spacer and skeleton rows are never focusable (no tabindex).
4
+ // biome-ignore-all lint/a11y/useFocusableInteractive: footer and empty-state cells are read, not navigated.
5
+ "use client";
6
+
7
+ import {
8
+ ArrowDown,
9
+ ArrowDownToLine,
10
+ ArrowUp,
11
+ ArrowUpToLine,
12
+ BarChart3,
13
+ ChevronDown,
14
+ ChevronLeft,
15
+ ChevronRight,
16
+ ChevronsLeft,
17
+ ChevronsRight,
18
+ CircleAlert,
19
+ Copy,
20
+ GripVertical,
21
+ PinOff,
22
+ } from "lucide-react";
23
+ import {
24
+ type ClipboardEvent,
25
+ type CSSProperties,
26
+ type DragEvent,
27
+ type FocusEvent,
28
+ type KeyboardEvent,
29
+ lazy,
30
+ type ReactNode,
31
+ type PointerEvent as ReactPointerEvent,
32
+ Suspense,
33
+ useCallback,
34
+ useEffect,
35
+ useId,
36
+ useLayoutEffect,
37
+ useMemo,
38
+ useRef,
39
+ useState,
40
+ } from "react";
41
+ import { Chip } from "../../atoms/chip/chip.js";
42
+ import { formatCurrency, formatNumber } from "../../charts/core/format.js";
43
+ import { Sparkline } from "../../charts/sparkline/sparkline.js";
44
+ import { cn } from "../../utils/cn.js";
45
+ import { computePinOffsets, type PinPosition, sanitizePinned } from "../table/pin.js";
46
+ import { type GridChartKind, type RangeChartData, rangeToChart } from "./chart-range.js";
47
+ import { buildHeaderGroups, groupPathOf } from "./column-groups.js";
48
+ import { ColumnMenu } from "./column-menu.js";
49
+ import type { DataSource } from "./data-source.js";
50
+ import type { DateStepMode } from "./fill-series.js";
51
+ import {
52
+ copyFill,
53
+ edgeScrollStep,
54
+ type FillBounds,
55
+ fillLanes,
56
+ type HandleExtent,
57
+ handleExtent,
58
+ isBackward,
59
+ seriesFill,
60
+ } from "./fill-series.js";
61
+ import {
62
+ applyAdvancedFilter,
63
+ countConditions,
64
+ emptyGroup,
65
+ type FilterGroup,
66
+ } from "./filter-builder.js";
67
+ import { FilterBuilder } from "./filter-builder-editor.js";
68
+ import { cellAddress, columnLabel, isFormulaText, shiftFormula } from "./formula.js";
69
+ import { FormulaBar } from "./formula-bar.js";
70
+ import { buildSheet, displayValue, isErrorText, type ParseCache } from "./formula-sheet.js";
71
+ import {
72
+ type FillDirection,
73
+ parseLooseNumber,
74
+ parseTSV,
75
+ planFill,
76
+ planPaste,
77
+ sameCellValue,
78
+ } from "./grid-edit.js";
79
+ import { GridToolbar } from "./grid-toolbar.js";
80
+ import {
81
+ type CellDelta,
82
+ type ChangeSource,
83
+ commandSize,
84
+ createHistory,
85
+ DEFAULT_HISTORY_DEPTH,
86
+ type HistoryCommand,
87
+ type HistorySummary,
88
+ pushCommand,
89
+ type RowRef,
90
+ redoCommand,
91
+ setHistoryDepth,
92
+ summarizeHistory,
93
+ undoCommand,
94
+ } from "./history.js";
95
+ import { type DataGridLabels, DEFAULT_DATA_GRID_LABELS } from "./labels.js";
96
+ import {
97
+ aggregateValues,
98
+ buildDisplayRows,
99
+ columnAlign,
100
+ computeAggregates,
101
+ countActiveFilters,
102
+ distinctValues,
103
+ fillColumnWidths,
104
+ filterRows,
105
+ formatAggregate,
106
+ formatCellValue,
107
+ getCellValue,
108
+ rangeBetween,
109
+ sortRows,
110
+ toCSV,
111
+ toggleSort,
112
+ } from "./model.js";
113
+ import {
114
+ buildPivot,
115
+ isPivotValid,
116
+ type PivotConfig,
117
+ type PivotRow,
118
+ pivotColumns,
119
+ pivotFooter,
120
+ } from "./pivot.js";
121
+ import {
122
+ type CellRange,
123
+ extendRange,
124
+ inRange,
125
+ rangeBounds,
126
+ rangeStats,
127
+ rangeToTSV,
128
+ } from "./range.js";
129
+ import {
130
+ insertDetailRows,
131
+ moveRowBy,
132
+ moveRowTo,
133
+ NO_PINNED_ROWS,
134
+ type PinnedRows,
135
+ pinnedSideOf,
136
+ type RowPinSide,
137
+ splitPinnedRows,
138
+ togglePinnedRow,
139
+ toggleSetMember,
140
+ } from "./rows.js";
141
+ import { EMPTY_PIVOT, GridToolPanel } from "./tool-panel.js";
142
+ import { buildTreeRows, withAncestors } from "./tree.js";
143
+ import type {
144
+ CellValue,
145
+ DataGridColumn,
146
+ Density,
147
+ DisplayRow,
148
+ EditorKind,
149
+ Filters,
150
+ FilterValue,
151
+ PinSide,
152
+ SortRule,
153
+ } from "./types.js";
154
+ import { useDataSource } from "./use-data-source.js";
155
+ import { useGridLayout } from "./use-grid-layout.js";
156
+ import {
157
+ computeVariableWindow,
158
+ computeVirtualWindow,
159
+ pageCount,
160
+ pageStep,
161
+ scrollLeftToReveal,
162
+ scrollTopToRevealSpan,
163
+ } from "./virtual.js";
164
+ import { gridToSheet, xlsxFilename } from "./xlsx-export.js";
165
+
166
+ export interface CellChange<T> {
167
+ row: T;
168
+ rowId: string;
169
+ columnId: string;
170
+ value: CellValue;
171
+ previous: CellValue;
172
+ /** Where the change comes from; `undo` and `redo` replay an earlier change in reverse or again. */
173
+ source?: ChangeSource;
174
+ }
175
+
176
+ /** Rows added or removed by the toolbar buttons, or by an undo / redo of them. */
177
+ export interface RowsChange<T> {
178
+ type: "add" | "remove";
179
+ /** Ascending by `index` (the position in the `data` the grid was given). */
180
+ rows: ReadonlyArray<{ row: T; rowId: string; index: number }>;
181
+ source: "edit" | "undo" | "redo";
182
+ }
183
+
184
+ export interface RowReorderEvent {
185
+ rowId: string;
186
+ fromIndex: number;
187
+ toIndex: number;
188
+ /** Ids of all rows in the new order. */
189
+ order: string[];
190
+ }
191
+
192
+ export interface ExportPayload {
193
+ csv: string;
194
+ filename: string;
195
+ rowCount: number;
196
+ }
197
+
198
+ const ChartPanel = lazy(() =>
199
+ import("./chart-panel.js").then((module) => ({ default: module.ChartPanel })),
200
+ );
201
+
202
+ export interface XlsxExportPayload {
203
+ /** The .xlsx file. */
204
+ bytes: Uint8Array;
205
+ filename: string;
206
+ rowCount: number;
207
+ }
208
+
209
+ export interface DataGridProps<T> {
210
+ /** Rows to show. Optional with `dataSource`, which loads them. */
211
+ data?: readonly T[];
212
+ columns: readonly DataGridColumn<T>[];
213
+ /** Accessible name of the grid. */
214
+ "aria-label": string;
215
+ /** Stable row id (selection, editing, keys). The data index by default. */
216
+ getRowId?: (row: T, index: number) => string;
217
+ /** Maximum height of the scrolling body in px (virtualized mode). */
218
+ height?: number;
219
+ /** Switches from virtualized scrolling to pagination with this page size. */
220
+ pageSize?: number;
221
+ pageSizeOptions?: readonly number[];
222
+ /** localStorage key that remembers column order, widths, visibility, pins and density. */
223
+ storageKey?: string;
224
+
225
+ sort?: readonly SortRule[];
226
+ defaultSort?: readonly SortRule[];
227
+ onSortChange?: (sort: SortRule[]) => void;
228
+ filters?: Filters;
229
+ defaultFilters?: Filters;
230
+ onFiltersChange?: (filters: Filters) => void;
231
+ groupBy?: readonly string[];
232
+ defaultGroupBy?: readonly string[];
233
+ onGroupByChange?: (groupBy: string[]) => void;
234
+ defaultPinned?: { left?: readonly string[]; right?: readonly string[] };
235
+ defaultHidden?: readonly string[];
236
+ density?: Density;
237
+ defaultDensity?: Density;
238
+ onDensityChange?: (density: Density) => void;
239
+
240
+ /** Adds a checkbox column; Shift selects a range. */
241
+ selectable?: boolean;
242
+ selectedRowIds?: readonly string[];
243
+ defaultSelectedRowIds?: readonly string[];
244
+ onSelectionChange?: (ids: string[]) => void;
245
+
246
+ /** Called when an inline edit is committed with a different value. The grid does not mutate `data`. */
247
+ onCellChange?: (change: CellChange<T>) => void;
248
+ /**
249
+ * Called once with every cell a paste, fill, undo or redo changes. Without it those changes go to
250
+ * `onCellChange` one by one, which loses updates if the parent sets state from stale data.
251
+ */
252
+ onCellsChange?: (changes: CellChange<T>[]) => void;
253
+ /** Undo / redo of cell edits, paste, fill and row changes: buttons plus Ctrl / Cmd + Z, Shift + Ctrl / Cmd + Z, Ctrl + Y. */
254
+ history?: boolean;
255
+ /** Commands kept for undo (and for redo). 100 by default. */
256
+ historyDepth?: number;
257
+ onHistoryChange?: (history: HistorySummary) => void;
258
+ /**
259
+ * Spreadsheet formulas: a cell whose text starts with `=` is evaluated (`=B2*C2`, `=SUM(D2:D9)`,
260
+ * `=IF(E2>100,"alto","bajo")`). Columns are lettered in the order of `columns` and rows are
261
+ * numbered in the order of `data`; a formula bar shows the raw content of the focused cell.
262
+ * Off with `dataSource` and `pivot`.
263
+ */
264
+ formulas?: boolean;
265
+ /** Adds the "add row" button: the new row is appended through `onRowsChange`. */
266
+ createRow?: () => T;
267
+ /** Rows added (`createRow`), removed (the toolbar button removes the selected rows) or restored by undo. */
268
+ onRowsChange?: (change: RowsChange<T>) => void;
269
+
270
+ /**
271
+ * Server-side rows. Sort, filters and quick search are sent to `getRows` instead of being
272
+ * applied here; with `pageSize` the grid pages through blocks of that size, without it rows are
273
+ * loaded as you scroll. Grouping, tree data, row pinning, pivot and detail rows are off.
274
+ */
275
+ dataSource?: DataSource<T>;
276
+ /** Blocks kept in memory in infinite mode before the least recently used are dropped. */
277
+ maxBlocks?: number;
278
+
279
+ /** Shows `data` as a tree: `getParentId` links a row to its parent (nullish for roots). */
280
+ tree?: { getParentId: (row: T) => string | null | undefined };
281
+ /** Expanded row ids: tree nodes and rows with a detail panel. */
282
+ expandedRowIds?: readonly string[];
283
+ defaultExpandedRowIds?: readonly string[];
284
+ onExpandedRowsChange?: (ids: string[]) => void;
285
+ /** Detail panel (master / detail) rendered under an expanded row. */
286
+ renderDetail?: (row: T, rowId: string) => ReactNode;
287
+ /** Height of a detail panel in px (fixed so rows stay virtualizable). 240 by default. */
288
+ detailHeight?: number | ((row: T) => number);
289
+
290
+ /** Adds pin-to-top / pin-to-bottom buttons to rows; pinned rows stay in view while scrolling. */
291
+ rowPinning?: boolean;
292
+ pinnedRows?: PinnedRows;
293
+ defaultPinnedRows?: PinnedRows;
294
+ onPinnedRowsChange?: (pinned: PinnedRows) => void;
295
+ /** Drag handle and Alt + arrows to reorder rows; works without sort, filters or grouping. */
296
+ rowReorder?: boolean;
297
+ /** Called with the new order; the grid does not mutate `data`. */
298
+ onRowReorder?: (event: RowReorderEvent) => void;
299
+
300
+ /** Rectangular cell selection by drag, Shift + click or Shift + arrows, with Ctrl + C as TSV. */
301
+ rangeSelection?: boolean;
302
+ /**
303
+ * How the fill handle reads series. `dateStep`: "auto" (default) continues dates by months when they
304
+ * share the day of the month and by days otherwise; "day" and "month" force one unit.
305
+ */
306
+ fillSeries?: { dateStep?: DateStepMode };
307
+ /**
308
+ * Adds a "Graficar" button to the range status bar (turns `rangeSelection` on): the selected cells
309
+ * open a bar, line, area or pie chart under the grid. The text column of the range names the
310
+ * categories and each numeric column is a series. The chart code is loaded when first used.
311
+ */
312
+ charts?: boolean;
313
+
314
+ /** Advanced filter (AND / OR groups of conditions) applied after the column filters. */
315
+ advancedFilter?: FilterGroup;
316
+ defaultAdvancedFilter?: FilterGroup;
317
+ onAdvancedFilterChange?: (filter: FilterGroup) => void;
318
+ /** Adds the advanced filter button to the toolbar. */
319
+ showAdvancedFilter?: boolean;
320
+
321
+ /** Side panel to show, hide and reorder columns, group rows and set up a pivot. */
322
+ toolPanel?: boolean;
323
+ defaultToolPanelOpen?: boolean;
324
+ /** Pivot: row fields x pivot column x aggregated value. */
325
+ pivot?: PivotConfig;
326
+ defaultPivot?: PivotConfig;
327
+ onPivotChange?: (pivot: PivotConfig) => void;
328
+
329
+ loading?: boolean;
330
+ /** Shown when there are no rows (a filtered-out message is used when filters are active). */
331
+ emptyMessage?: ReactNode;
332
+ /** Footer with the column aggregates. On when some column has `aggregate`. */
333
+ showFooter?: boolean;
334
+ /** Hides search, chips, density, columns and export. */
335
+ hideToolbar?: boolean;
336
+ /** CSV file name for the export button; `false` hides the button. */
337
+ exportFilename?: string | false;
338
+ /** Called with the CSV right before it is downloaded. */
339
+ onExport?: (payload: ExportPayload) => void;
340
+ /**
341
+ * Adds an "Exportar XLSX" button: a file name (`"ventas.xlsx"`) or `true` for `datos.xlsx`. Same
342
+ * rows and columns as the CSV export, with numbers as numbers and dates as dates. The writer
343
+ * (`@jcsoftdev/ui/xlsx`) is loaded when the button is used, so it costs nothing until then.
344
+ */
345
+ exportXlsx?: string | boolean;
346
+ /** Called with the file right before it is downloaded. */
347
+ onExportXlsx?: (payload: XlsxExportPayload) => void;
348
+ toolbarActions?: ReactNode;
349
+ labels?: Partial<DataGridLabels>;
350
+ className?: string;
351
+ }
352
+
353
+ const ROW_HEIGHT: Record<Density, number> = { compact: 32, normal: 40, comfortable: 48 };
354
+ const SELECT_ID = "__select";
355
+ const SELECT_WIDTH = 44;
356
+ const TOOLS_ID = "__tools";
357
+ const TOOL_SLOT = 26;
358
+ const GROUP_HEADER_HEIGHT = 32;
359
+ const DEFAULT_DETAIL_HEIGHT = 240;
360
+ const DEFAULT_BLOCK_SIZE = 50;
361
+ const EMPTY_DATA: readonly never[] = [];
362
+ const SKELETON_ROWS = 8;
363
+ const EMPTY_SORT: readonly SortRule[] = [];
364
+ const EMPTY_FILTERS: Filters = {};
365
+ const EMPTY_LIST: readonly string[] = [];
366
+ const EMPTY_ADVANCED: FilterGroup = emptyGroup("and", "root");
367
+
368
+ interface CellEdit<T> {
369
+ row: T;
370
+ rowId: string;
371
+ column: DataGridColumn<T>;
372
+ value: CellValue;
373
+ previous: CellValue;
374
+ }
375
+
376
+ type RenderColumn<T> =
377
+ | { kind: "select" }
378
+ | { kind: "tools" }
379
+ | { kind: "data"; column: DataGridColumn<T> };
380
+ type Zone = PinSide | "center";
381
+
382
+ function idOfColumn<T>(entry: RenderColumn<T>): string {
383
+ if (entry.kind === "select") return SELECT_ID;
384
+ return entry.kind === "tools" ? TOOLS_ID : entry.column.id;
385
+ }
386
+
387
+ function useControllable<V>(
388
+ value: V | undefined,
389
+ initial: V,
390
+ onChange?: (value: V) => void,
391
+ ): [V, (next: V) => void] {
392
+ const [internal, setInternal] = useState(initial);
393
+ const controlled = value !== undefined;
394
+ const set = useCallback(
395
+ (next: V) => {
396
+ if (!controlled) setInternal(next);
397
+ onChange?.(next);
398
+ },
399
+ [controlled, onChange],
400
+ );
401
+ return [controlled ? value : internal, set];
402
+ }
403
+
404
+ /** Starts a CSV download in the browser (no-op on the server). */
405
+ export function downloadCSV(filename: string, csv: string): void {
406
+ downloadFile(filename, csv, "text/csv;charset=utf-8");
407
+ }
408
+
409
+ /** Starts a file download in the browser (no-op on the server). */
410
+ export function downloadFile(filename: string, data: BlobPart, type: string): void {
411
+ if (typeof document === "undefined") return;
412
+ const url = URL.createObjectURL(new Blob([data], { type }));
413
+ const link = document.createElement("a");
414
+ link.href = url;
415
+ link.download = filename;
416
+ document.body.append(link);
417
+ link.click();
418
+ link.remove();
419
+ setTimeout(() => URL.revokeObjectURL(url), 0);
420
+ }
421
+
422
+ const ARROW_STEPS: Record<string, { dr: number; dc: number } | undefined> = {
423
+ ArrowUp: { dr: -1, dc: 0 },
424
+ ArrowDown: { dr: 1, dc: 0 },
425
+ ArrowLeft: { dr: 0, dc: -1 },
426
+ ArrowRight: { dr: 0, dc: 1 },
427
+ };
428
+
429
+ /** Copies text to the clipboard, with a textarea fallback for contexts without the async API. */
430
+ function writeClipboard(text: string): void {
431
+ if (typeof navigator !== "undefined" && navigator.clipboard?.writeText) {
432
+ navigator.clipboard.writeText(text).catch(() => undefined);
433
+ return;
434
+ }
435
+ if (typeof document === "undefined") return;
436
+ const area = document.createElement("textarea");
437
+ area.value = text;
438
+ area.style.position = "fixed";
439
+ area.style.opacity = "0";
440
+ document.body.append(area);
441
+ area.select();
442
+ try {
443
+ document.execCommand("copy");
444
+ } finally {
445
+ area.remove();
446
+ }
447
+ }
448
+
449
+ function editorOf<T>(column: DataGridColumn<T>): EditorKind {
450
+ if (column.editor) return column.editor;
451
+ if (column.options?.length) return "select";
452
+ return column.type === "number" || column.type === "currency" ? "number" : "text";
453
+ }
454
+
455
+ function canEdit<T>(column: DataGridColumn<T>, row: T): boolean {
456
+ return typeof column.editable === "function" ? column.editable(row) : column.editable === true;
457
+ }
458
+
459
+ const alignClass = { start: "text-left", center: "text-center", end: "text-right" } as const;
460
+ const justifyClass = {
461
+ start: "justify-start",
462
+ center: "justify-center",
463
+ end: "justify-end",
464
+ } as const;
465
+
466
+ /**
467
+ * Data grid with sorting, filters, quick search, column pinning, resizing, reordering and
468
+ * visibility, row selection, grouping with aggregates, inline editing, virtualized rows or
469
+ * pagination, CSV export, and full keyboard navigation (ARIA grid pattern, roving tabindex).
470
+ * It adapts to its container width: the toolbar wraps and the grid scrolls horizontally with
471
+ * its pinned columns kept in place.
472
+ */
473
+ export function DataGrid<T>({
474
+ data: dataProp = EMPTY_DATA as readonly T[],
475
+ columns: columnsProp,
476
+ "aria-label": ariaLabel,
477
+ getRowId: getRowIdProp,
478
+ height = 480,
479
+ pageSize: pageSizeProp,
480
+ pageSizeOptions = [25, 50, 100],
481
+ storageKey,
482
+ sort: sortProp,
483
+ defaultSort = EMPTY_SORT,
484
+ onSortChange,
485
+ filters: filtersProp,
486
+ defaultFilters = EMPTY_FILTERS,
487
+ onFiltersChange,
488
+ groupBy: groupByProp,
489
+ defaultGroupBy = EMPTY_LIST,
490
+ onGroupByChange,
491
+ defaultPinned,
492
+ defaultHidden,
493
+ density: densityProp,
494
+ defaultDensity = "normal",
495
+ onDensityChange,
496
+ selectable = false,
497
+ selectedRowIds,
498
+ defaultSelectedRowIds = EMPTY_LIST,
499
+ onSelectionChange,
500
+ onCellChange,
501
+ onCellsChange,
502
+ formulas = false,
503
+ history: historyEnabled = false,
504
+ historyDepth = DEFAULT_HISTORY_DEPTH,
505
+ onHistoryChange,
506
+ createRow,
507
+ onRowsChange,
508
+ dataSource,
509
+ maxBlocks,
510
+ tree,
511
+ expandedRowIds,
512
+ defaultExpandedRowIds = EMPTY_LIST,
513
+ onExpandedRowsChange,
514
+ renderDetail,
515
+ detailHeight = DEFAULT_DETAIL_HEIGHT,
516
+ rowPinning = false,
517
+ pinnedRows,
518
+ defaultPinnedRows = NO_PINNED_ROWS,
519
+ onPinnedRowsChange,
520
+ rowReorder = false,
521
+ onRowReorder,
522
+ rangeSelection: rangeSelectionProp = false,
523
+ fillSeries: fillSeriesOptions,
524
+ charts = false,
525
+ advancedFilter: advancedFilterProp,
526
+ defaultAdvancedFilter = EMPTY_ADVANCED,
527
+ onAdvancedFilterChange,
528
+ showAdvancedFilter = false,
529
+ toolPanel = false,
530
+ defaultToolPanelOpen = false,
531
+ pivot: pivotProp,
532
+ defaultPivot = EMPTY_PIVOT,
533
+ onPivotChange,
534
+ loading = false,
535
+ emptyMessage,
536
+ showFooter,
537
+ hideToolbar = false,
538
+ exportFilename = "datos.csv",
539
+ onExport,
540
+ exportXlsx = false,
541
+ onExportXlsx,
542
+ toolbarActions,
543
+ labels: labelsProp,
544
+ className,
545
+ }: DataGridProps<T>) {
546
+ const labels = useMemo(() => ({ ...DEFAULT_DATA_GRID_LABELS, ...labelsProp }), [labelsProp]);
547
+ const helpId = useId();
548
+ const scrollerRef = useRef<HTMLDivElement>(null);
549
+ const gridRef = useRef<HTMLTableElement>(null);
550
+
551
+ // ---- state -------------------------------------------------------------------------------
552
+ const [sort, setSortState] = useControllable<readonly SortRule[]>(
553
+ sortProp,
554
+ defaultSort,
555
+ onSortChange && ((next) => onSortChange([...next])),
556
+ );
557
+ const [filters, setFiltersState] = useControllable<Filters>(
558
+ filtersProp,
559
+ defaultFilters,
560
+ onFiltersChange,
561
+ );
562
+ const [groupBy, setGroupByState] = useControllable<readonly string[]>(
563
+ groupByProp,
564
+ defaultGroupBy,
565
+ onGroupByChange && ((next) => onGroupByChange([...next])),
566
+ );
567
+ const [selection, setSelection] = useControllable<readonly string[]>(
568
+ selectedRowIds,
569
+ defaultSelectedRowIds,
570
+ onSelectionChange && ((next) => onSelectionChange([...next])),
571
+ );
572
+ const [expanded, setExpanded] = useControllable<readonly string[]>(
573
+ expandedRowIds,
574
+ defaultExpandedRowIds,
575
+ onExpandedRowsChange && ((next) => onExpandedRowsChange([...next])),
576
+ );
577
+ const [pinnedRowIds, setPinnedRowIds] = useControllable<PinnedRows>(
578
+ pinnedRows,
579
+ defaultPinnedRows,
580
+ onPinnedRowsChange,
581
+ );
582
+ const [advanced, setAdvanced] = useControllable<FilterGroup>(
583
+ advancedFilterProp,
584
+ defaultAdvancedFilter,
585
+ onAdvancedFilterChange,
586
+ );
587
+ const [pivotConfig, setPivotConfig] = useControllable<PivotConfig>(
588
+ pivotProp,
589
+ defaultPivot,
590
+ onPivotChange,
591
+ );
592
+ const [search, setSearch] = useState("");
593
+ const [collapsed, setCollapsed] = useState<ReadonlySet<string>>(() => new Set());
594
+ const [page, setPage] = useState(0);
595
+ const [pageSize, setPageSize] = useState(pageSizeProp ?? 25);
596
+ const [active, setActive] = useState({ r: 0, c: 0 });
597
+ const [menuColumn, setMenuColumn] = useState<string | null>(null);
598
+ const [editing, setEditing] = useState<{
599
+ key: string;
600
+ columnId: string;
601
+ draft: string;
602
+ error?: string;
603
+ /** Place the caret at the end instead of selecting the text (typing `=` to start a formula). */
604
+ caretEnd?: boolean;
605
+ } | null>(null);
606
+ const [scrollTop, setScrollTop] = useState(0);
607
+ const [scrolled, setScrolled] = useState({ start: false, end: false });
608
+ const [dropTarget, setDropTarget] = useState<{ id: string; place: "before" | "after" } | null>(
609
+ null,
610
+ );
611
+ const [dropRow, setDropRow] = useState<{ key: string; place: "before" | "after" } | null>(null);
612
+ const [cellRange, setCellRange] = useState<CellRange | null>(null);
613
+ /** Range a drag of the fill handle would reach, drawn while the pointer is down. */
614
+ const rangeB = cellRange ? rangeBounds(cellRange) : null;
615
+ const [fillPreview, setFillPreview] = useState<FillBounds | null>(null);
616
+ const [panelOpen, setPanelOpen] = useState(defaultToolPanelOpen);
617
+ const [announcement, setAnnouncement] = useState("");
618
+ const editingRef = useRef(editing);
619
+ editingRef.current = editing;
620
+ const pendingFocus = useRef(false);
621
+ const anchor = useRef<string | null>(null);
622
+ const resizing = useRef(false);
623
+ const dragId = useRef<string | null>(null);
624
+ const rowDrag = useRef<string | null>(null);
625
+
626
+ const rangeSelection = rangeSelectionProp || charts;
627
+ const [chart, setChart] = useState<{
628
+ data: RangeChartData | null;
629
+ kind: GridChartKind;
630
+ cells: number;
631
+ currency: boolean;
632
+ } | null>(null);
633
+ const paged = pageSizeProp !== undefined;
634
+ const sourceActive = dataSource !== undefined;
635
+
636
+ // ---- pivot: the grid shows a derived table of rows and columns ---------------------------
637
+ const pivotResult = useMemo(
638
+ () =>
639
+ !sourceActive && isPivotValid(pivotConfig, columnsProp)
640
+ ? buildPivot(dataProp, columnsProp, pivotConfig)
641
+ : null,
642
+ [sourceActive, pivotConfig, columnsProp, dataProp],
643
+ );
644
+ const pivotActive = pivotResult !== null;
645
+ const structured = !sourceActive && !pivotActive;
646
+ const formulasOn = formulas && structured;
647
+ const formulaCache = useRef<ParseCache>(new Map());
648
+ const sheet = useMemo(
649
+ () =>
650
+ formulasOn && columnsProp.length > 0
651
+ ? buildSheet({
652
+ rows: dataProp.length,
653
+ cols: columnsProp.length,
654
+ read: (r, c) => getCellValue(dataProp[r] as T, columnsProp[c] as DataGridColumn<T>),
655
+ cache: formulaCache.current,
656
+ })
657
+ : null,
658
+ [formulasOn, dataProp, columnsProp],
659
+ );
660
+ const sourceIndex = useMemo(() => {
661
+ if (!sheet) return null;
662
+ const rows = new Map<T, number>();
663
+ dataProp.forEach((row, index) => {
664
+ rows.set(row, index);
665
+ });
666
+ const cols = new Map(columnsProp.map((column, index) => [column.id, index]));
667
+ return { rows, cols };
668
+ }, [sheet, dataProp, columnsProp]);
669
+ const columns = useMemo(() => {
670
+ if (pivotResult) {
671
+ return pivotColumns(
672
+ columnsProp,
673
+ pivotConfig,
674
+ pivotResult,
675
+ labels.total,
676
+ ) as readonly DataGridColumn<T>[];
677
+ }
678
+ if (!sheet || !sourceIndex) return columnsProp;
679
+ // Formula cells read as their result everywhere (sort, filter, totals, CSV); editing reads the raw text.
680
+ return columnsProp.map((column, c) => ({
681
+ ...column,
682
+ accessor: (row: T): CellValue => {
683
+ const r = sourceIndex.rows.get(row);
684
+ const shown = r === undefined ? undefined : displayValue(sheet, r, c);
685
+ return shown === undefined ? getCellValue(row, column) : shown;
686
+ },
687
+ })) as readonly DataGridColumn<T>[];
688
+ }, [pivotResult, columnsProp, pivotConfig, labels.total, sheet, sourceIndex]);
689
+ const data = useMemo(
690
+ () => (pivotResult ? pivotResult.rows : dataProp) as readonly T[],
691
+ [pivotResult, dataProp],
692
+ );
693
+ const getRowId = useMemo(
694
+ () => (pivotResult ? (row: T) => (row as unknown as PivotRow).id : getRowIdProp),
695
+ [pivotResult, getRowIdProp],
696
+ );
697
+
698
+ const layoutDefaults = useMemo(
699
+ () => ({ pinned: defaultPinned, hidden: defaultHidden, density: defaultDensity }),
700
+ [defaultPinned, defaultHidden, defaultDensity],
701
+ );
702
+ const grid = useGridLayout(columns, layoutDefaults, pivotActive ? undefined : storageKey);
703
+ const { layout } = grid;
704
+ const density = densityProp ?? layout.density ?? defaultDensity;
705
+ const rowHeight = ROW_HEIGHT[density];
706
+ const headerHeight = Math.max(rowHeight, 36);
707
+ const firstRow = Math.floor(scrollTop / rowHeight);
708
+
709
+ // ---- columns -----------------------------------------------------------------------------
710
+ const byId = useMemo(() => new Map(columns.map((column) => [column.id, column])), [columns]);
711
+ const { dataColumns, zoneOf, pinned } = useMemo(() => {
712
+ const visible = layout.order.filter((id) => byId.has(id) && !layout.hidden.includes(id));
713
+ const pins = sanitizePinned(layout.pinned, visible);
714
+ const left = visible.filter((id) => pins.left.includes(id));
715
+ const right = visible.filter((id) => pins.right.includes(id));
716
+ const center = visible.filter((id) => !left.includes(id) && !right.includes(id));
717
+ const zones = new Map<string, Zone>();
718
+ for (const id of left) zones.set(id, "left");
719
+ for (const id of right) zones.set(id, "right");
720
+ for (const id of center) zones.set(id, "center");
721
+ return {
722
+ dataColumns: [...left, ...center, ...right].map((id) => byId.get(id) as DataGridColumn<T>),
723
+ zoneOf: zones,
724
+ pinned: { left, right },
725
+ };
726
+ }, [layout.order, layout.hidden, layout.pinned, byId]);
727
+
728
+ const detailEnabled = !!renderDetail && structured && !tree;
729
+ const reorderEnabled = rowReorder && structured;
730
+ const pinningEnabled = rowPinning && structured;
731
+ const toolSlots = (detailEnabled ? 1 : 0) + (reorderEnabled ? 1 : 0) + (pinningEnabled ? 2 : 0);
732
+ const toolsWidth = toolSlots * TOOL_SLOT + 12;
733
+ const renderColumns = useMemo<RenderColumn<T>[]>(
734
+ () => [
735
+ ...(selectable ? [{ kind: "select" } as const] : []),
736
+ ...(toolSlots > 0 ? [{ kind: "tools" } as const] : []),
737
+ ...dataColumns.map((column) => ({ kind: "data", column }) as const),
738
+ ],
739
+ [selectable, toolSlots, dataColumns],
740
+ );
741
+ const leadingCount = renderColumns.length - dataColumns.length;
742
+ const fillerIndex = renderColumns.length - pinned.right.length;
743
+ const [containerWidth, setContainerWidth] = useState(0);
744
+ const reservedWidth = (selectable ? SELECT_WIDTH : 0) + (toolSlots > 0 ? toolsWidth : 0);
745
+ // Columns narrower than their container grow to fill it; user-resized ones keep their width.
746
+ const shownWidths = useMemo(
747
+ () =>
748
+ containerWidth > 0
749
+ ? fillColumnWidths(dataColumns, layout.widths, containerWidth, reservedWidth)
750
+ : layout.widths,
751
+ [containerWidth, dataColumns, layout.widths, reservedWidth],
752
+ );
753
+ const widthOf = useCallback(
754
+ (entry: RenderColumn<T>) =>
755
+ entry.kind === "select"
756
+ ? SELECT_WIDTH
757
+ : entry.kind === "tools"
758
+ ? toolsWidth
759
+ : (shownWidths[entry.column.id] ?? 160),
760
+ [shownWidths, toolsWidth],
761
+ );
762
+ const positions = useMemo(() => {
763
+ const widths: Record<string, number> = {
764
+ [SELECT_ID]: SELECT_WIDTH,
765
+ [TOOLS_ID]: toolsWidth,
766
+ ...shownWidths,
767
+ };
768
+ const left = [
769
+ ...(selectable ? [SELECT_ID] : []),
770
+ ...(toolSlots > 0 ? [TOOLS_ID] : []),
771
+ ...pinned.left,
772
+ ];
773
+ return computePinOffsets(widths, { left, right: pinned.right });
774
+ }, [shownWidths, pinned, selectable, toolSlots, toolsWidth]);
775
+ const totalWidth = renderColumns.reduce((sum, entry) => sum + widthOf(entry), 0);
776
+
777
+ // Multi-level headers: the levels come from the `group` path of the visible columns.
778
+ const headerLevels = useMemo(() => {
779
+ const paths = dataColumns.map((column) => groupPathOf(column));
780
+ const breaks = dataColumns
781
+ .map((column, index) =>
782
+ index > 0 && zoneOf.get(column.id) !== zoneOf.get(dataColumns[index - 1]?.id ?? "")
783
+ ? index
784
+ : -1,
785
+ )
786
+ .filter((index) => index >= 0);
787
+ return buildHeaderGroups(paths, breaks);
788
+ }, [dataColumns, zoneOf]);
789
+ const groupLevels = headerLevels.length;
790
+ const headerTop = groupLevels * GROUP_HEADER_HEIGHT;
791
+
792
+ // ---- rows --------------------------------------------------------------------------------
793
+ const blockSize = paged ? pageSize : (dataSource?.blockSize ?? DEFAULT_BLOCK_SIZE);
794
+ const remoteRange = paged
795
+ ? { start: page * pageSize, end: (page + 1) * pageSize }
796
+ : { start: Math.max(0, firstRow - 8), end: firstRow + Math.ceil(height / rowHeight) + 12 };
797
+ const remote = useDataSource(dataSource, {
798
+ sort,
799
+ filters,
800
+ search,
801
+ blockSize,
802
+ maxBlocks,
803
+ range: remoteRange,
804
+ });
805
+
806
+ // The first response of a remote source is still pending: show the skeleton.
807
+ const busy = loading || (sourceActive && remote.total === null && !remote.failed);
808
+
809
+ const localKeys = useMemo(() => {
810
+ const keys = new Map<T, string>();
811
+ data.forEach((row, index) => {
812
+ keys.set(row, getRowId ? getRowId(row, index) : String(index));
813
+ });
814
+ return keys;
815
+ }, [data, getRowId]);
816
+ const rowAt = remote.rowAt;
817
+ const remoteModel = useMemo(() => {
818
+ if (!sourceActive) return null;
819
+ const keys = new Map<T, string>();
820
+ const byKey = new Map<string, T>();
821
+ const rows: DisplayRow<T>[] = [];
822
+ const loaded: T[] = [];
823
+ const total = remote.total ?? 0;
824
+ for (let index = 0; index < total; index += 1) {
825
+ const row = rowAt(index);
826
+ if (row === undefined) {
827
+ rows.push({ kind: "placeholder", key: `~${index}`, index });
828
+ continue;
829
+ }
830
+ const key = getRowId ? getRowId(row, index) : String(index);
831
+ keys.set(row, key);
832
+ byKey.set(key, row);
833
+ loaded.push(row);
834
+ rows.push({ kind: "data", key, depth: 0, row });
835
+ }
836
+ return { keys, byKey, rows, loaded };
837
+ }, [sourceActive, remote.total, rowAt, getRowId]);
838
+ const keyOf = useCallback(
839
+ (row: T) => (remoteModel ? remoteModel.keys : localKeys).get(row) ?? "",
840
+ [remoteModel, localKeys],
841
+ );
842
+ const rowByKey = useMemo(
843
+ () => remoteModel?.byKey ?? new Map(data.map((row) => [keyOf(row), row])),
844
+ [remoteModel, data, keyOf],
845
+ );
846
+
847
+ const advancedCount = countConditions(advanced);
848
+ const filterActive = countActiveFilters(filters) > 0 || search.trim() !== "" || advancedCount > 0;
849
+ const filtered = useMemo(() => {
850
+ if (sourceActive) return [] as T[];
851
+ const base = filterRows(data, columns, filters, search);
852
+ return advancedCount > 0 ? applyAdvancedFilter(base, columns, advanced) : base;
853
+ }, [sourceActive, data, columns, filters, search, advanced, advancedCount]);
854
+ const treeOptions = useMemo(
855
+ () => (tree && structured ? { getKey: keyOf, getParentId: tree.getParentId } : null),
856
+ [tree, structured, keyOf],
857
+ );
858
+ const sorted = useMemo(() => {
859
+ if (remoteModel) return remoteModel.loaded;
860
+ const rows = treeOptions ? withAncestors(data, filtered, treeOptions) : filtered;
861
+ return sortRows(rows, columns, sort);
862
+ }, [remoteModel, treeOptions, data, filtered, columns, sort]);
863
+ const expandedSet = useMemo(() => new Set(expanded), [expanded]);
864
+ const treeResult = useMemo(
865
+ () =>
866
+ treeOptions
867
+ ? buildTreeRows(sorted, columns, {
868
+ ...treeOptions,
869
+ expanded: expandedSet,
870
+ forceExpand: filterActive,
871
+ })
872
+ : null,
873
+ [treeOptions, sorted, columns, expandedSet, filterActive],
874
+ );
875
+ const flat = useMemo<DisplayRow<T>[]>(() => {
876
+ if (remoteModel) return remoteModel.rows;
877
+ if (treeResult) return treeResult.rows;
878
+ return buildDisplayRows(
879
+ sorted,
880
+ columns,
881
+ structured ? groupBy : EMPTY_LIST,
882
+ collapsed,
883
+ keyOf,
884
+ sort,
885
+ );
886
+ }, [remoteModel, treeResult, sorted, columns, structured, groupBy, collapsed, keyOf, sort]);
887
+
888
+ const hasAggregates = columns.some((column) => column.aggregate);
889
+ const footer = !sourceActive && (showFooter ?? hasAggregates) && !busy && flat.length > 0;
890
+ const totals = useMemo(() => {
891
+ if (!footer) return {};
892
+ if (pivotResult) return pivotFooter(pivotResult);
893
+ if (treeResult) return computeAggregates(treeResult.leaves, columns);
894
+ const result: Record<string, number | null> = {};
895
+ for (const column of columns) {
896
+ if (!column.aggregate) continue;
897
+ const values = filtered.map((row) => getCellValue(row, column));
898
+ result[column.id] = aggregateValues(values, column.aggregate);
899
+ }
900
+ return result;
901
+ }, [footer, pivotResult, treeResult, columns, filtered]);
902
+
903
+ // Pinned rows leave the scrolling list; detail rows are added under their expanded row.
904
+ const split = useMemo(
905
+ () =>
906
+ pinningEnabled ? splitPinnedRows(flat, pinnedRowIds) : { top: [], middle: flat, bottom: [] },
907
+ [pinningEnabled, flat, pinnedRowIds],
908
+ );
909
+ const pages = pageCount(split.middle.length, pageSize);
910
+ const currentPage = Math.min(page, pages - 1);
911
+ const pageOffset = paged ? currentPage * pageSize : 0;
912
+ const pageRows = paged ? split.middle.slice(pageOffset, pageOffset + pageSize) : split.middle;
913
+ const heightOfDetail = useCallback(
914
+ (row: T) => (typeof detailHeight === "function" ? detailHeight(row) : detailHeight),
915
+ [detailHeight],
916
+ );
917
+ const middle = useMemo(
918
+ () => (detailEnabled ? insertDetailRows(pageRows, expandedSet, heightOfDetail) : pageRows),
919
+ [detailEnabled, pageRows, expandedSet, heightOfDetail],
920
+ );
921
+ const bodyRows = useMemo(
922
+ () =>
923
+ split.top.length > 0 || split.bottom.length > 0
924
+ ? [...split.top, ...middle, ...split.bottom]
925
+ : middle,
926
+ [split, middle],
927
+ );
928
+ const topCount = split.top.length;
929
+ const bottomCount = split.bottom.length;
930
+ const variableHeights = detailEnabled && middle.some((row) => row.kind === "detail");
931
+ const bodyViewport = Math.max(
932
+ rowHeight,
933
+ height - headerHeight - headerTop - (footer ? rowHeight : 0),
934
+ );
935
+ const win = useMemo(() => {
936
+ if (paged) return { start: 0, end: bodyRows.length, offsets: null as number[] | null };
937
+ if (variableHeights) {
938
+ return computeVariableWindow({
939
+ heights: bodyRows.map((row) => (row.kind === "detail" ? row.height : rowHeight)),
940
+ viewportHeight: bodyViewport,
941
+ scrollTop,
942
+ overscan: 8,
943
+ });
944
+ }
945
+ const uniform = computeVirtualWindow({
946
+ count: bodyRows.length,
947
+ rowHeight,
948
+ viewportHeight: bodyViewport,
949
+ scrollTop: firstRow * rowHeight,
950
+ overscan: 8,
951
+ });
952
+ return { start: uniform.start, end: uniform.end, offsets: null as number[] | null };
953
+ }, [paged, variableHeights, bodyRows, rowHeight, bodyViewport, scrollTop, firstRow]);
954
+ const bodyLength = bodyRows.length;
955
+ const midStart = Math.max(win.start, topCount);
956
+ const midEnd = Math.max(midStart, Math.min(win.end, bodyLength - bottomCount));
957
+ const offsetOf = (index: number) => (win.offsets ? (win.offsets[index] ?? 0) : index * rowHeight);
958
+ const gapTop = offsetOf(midStart) - offsetOf(topCount);
959
+ const gapBottom = offsetOf(bodyLength - bottomCount) - offsetOf(midEnd);
960
+ const isRendered = (index: number) =>
961
+ index < topCount ||
962
+ index >= bodyLength - bottomCount ||
963
+ (index >= win.start && index < win.end);
964
+
965
+ const selected = useMemo(() => new Set(selection), [selection]);
966
+ const visibleKeys = useMemo(
967
+ () => flat.filter((row) => row.kind === "data").map((row) => row.key),
968
+ [flat],
969
+ );
970
+ const allKeys = useMemo(() => sorted.map(keyOf), [sorted, keyOf]);
971
+ const selectedInView = allKeys.filter((key) => selected.has(key)).length;
972
+ const pivotValid = isPivotValid(pivotConfig, columnsProp);
973
+
974
+ // Row reordering only makes sense when the list is in its natural order.
975
+ const reorderAllowed =
976
+ reorderEnabled && sort.length === 0 && groupBy.length === 0 && !tree && !filterActive;
977
+
978
+ // ---- navigation geometry -----------------------------------------------------------------
979
+ const maxR = busy ? 0 : bodyRows.length;
980
+ const maxC = renderColumns.length - 1;
981
+ const activeR = Math.min(active.r, maxR);
982
+ const activeC = Math.min(active.c, Math.max(0, maxC));
983
+ const activeRendered = activeR === 0 || isRendered(activeR - 1);
984
+ const tabbable = activeRendered ? { r: activeR, c: activeC } : { r: 0, c: activeC };
985
+
986
+ const syncScroll = useCallback(() => {
987
+ const scroller = scrollerRef.current;
988
+ if (!scroller) return;
989
+ setScrollTop(Math.floor(scroller.scrollTop / rowHeight) * rowHeight);
990
+ const start = scroller.scrollLeft > 0;
991
+ const end = scroller.scrollLeft + scroller.clientWidth < scroller.scrollWidth - 1;
992
+ setScrolled((previous) =>
993
+ previous.start === start && previous.end === end ? previous : { start, end },
994
+ );
995
+ }, [rowHeight]);
996
+
997
+ useLayoutEffect(() => {
998
+ const scroller = scrollerRef.current;
999
+ const measure = () => {
1000
+ syncScroll();
1001
+ if (scroller) setContainerWidth(scroller.clientWidth);
1002
+ };
1003
+ measure();
1004
+ if (!scroller || typeof ResizeObserver === "undefined") return;
1005
+ const observer = new ResizeObserver(measure);
1006
+ observer.observe(scroller);
1007
+ return () => observer.disconnect();
1008
+ }, [syncScroll]);
1009
+
1010
+ useLayoutEffect(() => {
1011
+ if (!pendingFocus.current) return;
1012
+ pendingFocus.current = false;
1013
+ const cell = [
1014
+ ...(gridRef.current?.querySelectorAll<HTMLElement>(`[data-r="${tabbable.r}"]`) ?? []),
1015
+ ].find((node) => {
1016
+ const start = Number(node.dataset.c);
1017
+ const end = Number(node.dataset.cEnd ?? start);
1018
+ return tabbable.c >= start && tabbable.c <= end;
1019
+ });
1020
+ cell?.focus({ preventScroll: !paged });
1021
+ });
1022
+
1023
+ const reveal = useCallback(
1024
+ (r: number, c: number) => {
1025
+ const scroller = scrollerRef.current;
1026
+ if (!scroller) return;
1027
+ const index = r - 1;
1028
+ if (!paged && r > 0 && index >= topCount && index < bodyLength - bottomCount) {
1029
+ const item = bodyRows[index];
1030
+ const top = scrollTopToRevealSpan(
1031
+ win.offsets ? (win.offsets[index] ?? 0) : index * rowHeight,
1032
+ item?.kind === "detail" ? item.height : rowHeight,
1033
+ bodyViewport,
1034
+ scroller.scrollTop,
1035
+ topCount * rowHeight,
1036
+ bottomCount * rowHeight,
1037
+ );
1038
+ if (top !== scroller.scrollTop) {
1039
+ scroller.scrollTop = top;
1040
+ setScrollTop(Math.floor(top / rowHeight) * rowHeight);
1041
+ }
1042
+ }
1043
+ const entry = renderColumns[c];
1044
+ if (!entry || positions[idOfColumn(entry)]) return;
1045
+ let left = 0;
1046
+ for (let i = 0; i < c; i += 1) left += widthOf(renderColumns[i] as RenderColumn<T>);
1047
+ const pinnedLeft = renderColumns
1048
+ .filter((x) => positions[idOfColumn(x)]?.side === "left")
1049
+ .reduce((sum, x) => sum + widthOf(x), 0);
1050
+ const pinnedRight = renderColumns
1051
+ .filter((x) => positions[idOfColumn(x)]?.side === "right")
1052
+ .reduce((sum, x) => sum + widthOf(x), 0);
1053
+ scroller.scrollLeft = scrollLeftToReveal(
1054
+ left,
1055
+ widthOf(entry),
1056
+ scroller.clientWidth,
1057
+ scroller.scrollLeft,
1058
+ pinnedLeft,
1059
+ pinnedRight,
1060
+ );
1061
+ },
1062
+ [
1063
+ paged,
1064
+ rowHeight,
1065
+ bodyViewport,
1066
+ renderColumns,
1067
+ positions,
1068
+ widthOf,
1069
+ bodyRows,
1070
+ topCount,
1071
+ bottomCount,
1072
+ bodyLength,
1073
+ win.offsets,
1074
+ ],
1075
+ );
1076
+
1077
+ const focusCell = useCallback(
1078
+ (r: number, c: number, focus = true) => {
1079
+ const next = { r: Math.max(0, Math.min(maxR, r)), c: Math.max(0, Math.min(maxC, c)) };
1080
+ reveal(next.r, next.c);
1081
+ pendingFocus.current = focus;
1082
+ setActive(next);
1083
+ },
1084
+ [maxR, maxC, reveal],
1085
+ );
1086
+
1087
+ // ---- actions -----------------------------------------------------------------------------
1088
+ const resetPage = () => setPage(0);
1089
+ const applySort = (next: SortRule[]) => {
1090
+ setSortState(next);
1091
+ resetPage();
1092
+ };
1093
+ const setFilter = (id: string, filter: FilterValue | undefined) => {
1094
+ const next: Record<string, FilterValue> = { ...filters };
1095
+ if (filter) next[id] = filter;
1096
+ else delete next[id];
1097
+ setFiltersState(next);
1098
+ resetPage();
1099
+ };
1100
+ const setGroup = (id: string, on: boolean) => {
1101
+ setGroupByState(
1102
+ on ? [...groupBy.filter((x) => x !== id), id] : groupBy.filter((x) => x !== id),
1103
+ );
1104
+ resetPage();
1105
+ };
1106
+ const setDensity = (next: Density) => {
1107
+ grid.setDensity(next);
1108
+ onDensityChange?.(next);
1109
+ };
1110
+ const toggleGroupRow = (key: string) => {
1111
+ setCollapsed((previous) => {
1112
+ const next = new Set(previous);
1113
+ if (next.has(key)) next.delete(key);
1114
+ else next.add(key);
1115
+ return next;
1116
+ });
1117
+ };
1118
+ const toggleRow = (key: string, range: boolean) => {
1119
+ const willSelect = !selected.has(key);
1120
+ const keys = range ? rangeBetween(visibleKeys, anchor.current, key) : [key];
1121
+ const next = new Set(selected);
1122
+ for (const k of keys) {
1123
+ if (willSelect) next.add(k);
1124
+ else next.delete(k);
1125
+ }
1126
+ anchor.current = key;
1127
+ setSelection([...next]);
1128
+ };
1129
+ const toggleAll = () => {
1130
+ const next = new Set(selected);
1131
+ const everything = allKeys.length > 0 && selectedInView === allKeys.length;
1132
+ for (const key of allKeys) {
1133
+ if (everything) next.delete(key);
1134
+ else next.add(key);
1135
+ }
1136
+ setSelection([...next]);
1137
+ };
1138
+ const neighbourOf = (id: string, delta: number): string | null => {
1139
+ const index = dataColumns.findIndex((column) => column.id === id);
1140
+ const neighbour = dataColumns[index + delta];
1141
+ return neighbour && zoneOf.get(neighbour.id) === zoneOf.get(id) ? neighbour.id : null;
1142
+ };
1143
+ const moveColumn = (id: string, delta: number) => {
1144
+ const neighbour = neighbourOf(id, delta);
1145
+ if (!neighbour) return false;
1146
+ grid.moveTo(id, neighbour, delta < 0 ? "before" : "after");
1147
+ return true;
1148
+ };
1149
+ const exportCsv = () => {
1150
+ if (exportFilename === false) return;
1151
+ const rows =
1152
+ selectable && selection.length > 0
1153
+ ? sorted.filter((row) => selected.has(keyOf(row)))
1154
+ : sorted;
1155
+ const csv = toCSV(rows, dataColumns, { bom: true });
1156
+ onExport?.({ csv, filename: exportFilename, rowCount: rows.length });
1157
+ downloadCSV(exportFilename, csv);
1158
+ };
1159
+ const xlsxName = exportXlsx === true ? "datos.xlsx" : exportXlsx || null;
1160
+ const exportToXlsx = async () => {
1161
+ if (!xlsxName) return;
1162
+ const rows =
1163
+ selectable && selection.length > 0
1164
+ ? sorted.filter((row) => selected.has(keyOf(row)))
1165
+ : sorted;
1166
+ const filename = xlsxFilename(xlsxName);
1167
+ const { buildXlsx, XLSX_MIME } = await import("../../xlsx/index.js");
1168
+ const bytes = buildXlsx({
1169
+ sheets: [gridToSheet(rows, dataColumns, filename.replace(/\.xlsx$/i, ""))],
1170
+ creator: "@jcsoftdev/ui",
1171
+ });
1172
+ onExportXlsx?.({ bytes, filename, rowCount: rows.length });
1173
+ downloadFile(filename, bytes as BlobPart, XLSX_MIME);
1174
+ };
1175
+ const labelOfRow = (key: string) => {
1176
+ const row = rowByKey.get(key);
1177
+ const first = dataColumns[0];
1178
+ return row && first ? formatCellValue(getCellValue(row, first), first) || key : key;
1179
+ };
1180
+
1181
+ // ---- expansion, pinned rows and reordering -----------------------------------------------
1182
+ const setExpandedRow = (key: string, open?: boolean) => {
1183
+ const isOpen = expandedSet.has(key);
1184
+ if ((open ?? !isOpen) === isOpen) return;
1185
+ setExpanded([...toggleSetMember(expandedSet, key)]);
1186
+ };
1187
+ const pinRow = (key: string, side: RowPinSide) => {
1188
+ setPinnedRowIds(togglePinnedRow(pinnedRowIds, key, side));
1189
+ };
1190
+ const reorderRow = (
1191
+ key: string,
1192
+ target: { delta: number } | { id: string; place: "before" | "after" },
1193
+ ) => {
1194
+ if (!reorderAllowed) return false;
1195
+ const order = data.map(keyOf);
1196
+ const next =
1197
+ "delta" in target
1198
+ ? moveRowBy(order, key, target.delta)
1199
+ : moveRowTo(order, key, target.id, target.place);
1200
+ const fromIndex = order.indexOf(key);
1201
+ const toIndex = next.indexOf(key);
1202
+ if (fromIndex === -1 || fromIndex === toIndex) return false;
1203
+ onRowReorder?.({ rowId: key, fromIndex, toIndex, order: next });
1204
+ setAnnouncement(labels.rowMoved(labelOfRow(key), toIndex + 1, order.length));
1205
+ return true;
1206
+ };
1207
+
1208
+ // ---- cell ranges -------------------------------------------------------------------------
1209
+ const cellAt = (r: number, c: number) => {
1210
+ const row = bodyRows[r - 1];
1211
+ const entry = renderColumns[c];
1212
+ return row?.kind === "data" && entry?.kind === "data"
1213
+ ? { row: row.row, column: entry.column }
1214
+ : null;
1215
+ };
1216
+ const copyRange = () => {
1217
+ if (!cellRange) return;
1218
+ const bounds = rangeBounds(cellRange);
1219
+ const text = rangeToTSV(bounds, (r, c) => {
1220
+ const cell = cellAt(r, c);
1221
+ return cell ? formatCellValue(getCellValue(cell.row, cell.column), cell.column) : null;
1222
+ });
1223
+ writeClipboard(text);
1224
+ const cells = (bounds.r1 - bounds.r0 + 1) * (bounds.c1 - bounds.c0 + 1);
1225
+ setAnnouncement(labels.rangeCopied(cells));
1226
+ };
1227
+ const openChart = () => {
1228
+ if (!cellRange) return;
1229
+ const bounds = rangeBounds(cellRange);
1230
+ const headers: string[] = [];
1231
+ const columnIds: string[] = [];
1232
+ for (let c = bounds.c0; c <= bounds.c1; c += 1) {
1233
+ const entry = renderColumns[c];
1234
+ headers.push(entry?.kind === "data" ? entry.column.header : "");
1235
+ columnIds.push(entry ? idOfColumn(entry) : String(c));
1236
+ }
1237
+ const cells = [];
1238
+ for (let r = bounds.r0; r <= bounds.r1; r += 1) {
1239
+ const line = [];
1240
+ for (let c = bounds.c0; c <= bounds.c1; c += 1) {
1241
+ const cell = cellAt(r, c);
1242
+ line.push(
1243
+ cell
1244
+ ? {
1245
+ value: getCellValue(cell.row, cell.column),
1246
+ text: formatCellValue(getCellValue(cell.row, cell.column), cell.column),
1247
+ }
1248
+ : null,
1249
+ );
1250
+ }
1251
+ cells.push(line);
1252
+ }
1253
+ const data = rangeToChart({ headers, columnIds, cells, rowLabel: labels.chartRow });
1254
+ const currency =
1255
+ !!data &&
1256
+ data.series.length > 0 &&
1257
+ data.series.every((series) => byId.get(series.id)?.type === "currency");
1258
+ setChart({
1259
+ data,
1260
+ kind: "bar",
1261
+ cells: (bounds.r1 - bounds.r0 + 1) * (bounds.c1 - bounds.c0 + 1),
1262
+ currency,
1263
+ });
1264
+ setAnnouncement(data ? labels.chartTitle(rangeCellCount) : labels.chartNoData);
1265
+ };
1266
+ const onCellPointerDown = (event: ReactPointerEvent<HTMLElement>, r: number, c: number) => {
1267
+ if (
1268
+ !rangeSelection ||
1269
+ event.button !== 0 ||
1270
+ editingRef.current ||
1271
+ event.pointerType === "touch"
1272
+ )
1273
+ return;
1274
+ if (
1275
+ (event.target as HTMLElement).closest("button,input,select,a,textarea,[data-detail-content]")
1276
+ )
1277
+ return;
1278
+ if (event.shiftKey && cellRange) {
1279
+ setCellRange({ anchor: cellRange.anchor, focus: { r, c } });
1280
+ return;
1281
+ }
1282
+ setCellRange({ anchor: { r, c }, focus: { r, c } });
1283
+ const move = (moveEvent: PointerEvent) => {
1284
+ const cell = document
1285
+ .elementFromPoint(moveEvent.clientX, moveEvent.clientY)
1286
+ ?.closest<HTMLElement>("td[data-r]");
1287
+ if (!cell || !gridRef.current?.contains(cell)) return;
1288
+ const nextR = Number(cell.dataset.r);
1289
+ const nextC = Number(cell.dataset.c);
1290
+ setCellRange((previous) =>
1291
+ previous && (previous.focus.r !== nextR || previous.focus.c !== nextC)
1292
+ ? { anchor: previous.anchor, focus: { r: nextR, c: nextC } }
1293
+ : previous,
1294
+ );
1295
+ };
1296
+ const end = () => {
1297
+ window.removeEventListener("pointermove", move);
1298
+ window.removeEventListener("pointerup", end);
1299
+ window.removeEventListener("pointercancel", end);
1300
+ };
1301
+ window.addEventListener("pointermove", move);
1302
+ window.addEventListener("pointerup", end);
1303
+ window.addEventListener("pointercancel", end);
1304
+ };
1305
+
1306
+ // ---- editing -----------------------------------------------------------------------------
1307
+ const rawValue = (row: T, column: DataGridColumn<T>): CellValue =>
1308
+ getCellValue(row, columnsProp.find((x) => x.id === column.id) ?? column);
1309
+ /** Text typed (or pasted) into a cell to the value to store, or the reason it is refused. */
1310
+ const coerceDraft = (
1311
+ column: DataGridColumn<T>,
1312
+ row: T,
1313
+ draft: string,
1314
+ loose = false,
1315
+ ): { value: CellValue } | { error: string } => {
1316
+ let value: CellValue = draft;
1317
+ if (formulasOn && isFormulaText(draft)) return { value: draft.trim() };
1318
+ if (editorOf(column) === "number") {
1319
+ const number = loose ? parseLooseNumber(draft) : Number.parseFloat(draft.replace(",", "."));
1320
+ if (number !== null && Number.isFinite(number)) value = number;
1321
+ else return { error: labels.invalidCell };
1322
+ }
1323
+ const error = column.validate?.(value, row);
1324
+ return error ? { error } : { value };
1325
+ };
1326
+
1327
+ // ---- history -----------------------------------------------------------------------------
1328
+ const historyRef = useRef(createHistory<T>(historyDepth));
1329
+ const [historySummary, setHistorySummary] = useState<HistorySummary>(() =>
1330
+ summarizeHistory(historyRef.current),
1331
+ );
1332
+ const syncHistory = (next: typeof historyRef.current) => {
1333
+ historyRef.current = next;
1334
+ const summary = summarizeHistory(next);
1335
+ setHistorySummary(summary);
1336
+ onHistoryChange?.(summary);
1337
+ };
1338
+ // biome-ignore lint/correctness/useExhaustiveDependencies: only a new depth trims the stack.
1339
+ useEffect(() => {
1340
+ if (historyRef.current.depth !== Math.max(1, Math.floor(historyDepth))) {
1341
+ syncHistory(setHistoryDepth(historyRef.current, historyDepth));
1342
+ }
1343
+ }, [historyDepth]);
1344
+ const record = (command: HistoryCommand<T>) => {
1345
+ if (historyEnabled) syncHistory(pushCommand(historyRef.current, command));
1346
+ };
1347
+ const canWrite = !!(onCellChange || onCellsChange);
1348
+ /** A single change goes to `onCellChange`; several go to `onCellsChange` when it exists. */
1349
+ const emitChanges = (changes: CellChange<T>[]) => {
1350
+ if (changes.length === 0) return;
1351
+ if (changes.length === 1 && onCellChange) onCellChange(changes[0] as CellChange<T>);
1352
+ else if (onCellsChange) onCellsChange(changes);
1353
+ else for (const change of changes) onCellChange?.(change);
1354
+ };
1355
+ const commitChanges = (items: readonly CellEdit<T>[], source: "edit" | "paste" | "fill") => {
1356
+ if (items.length === 0) return;
1357
+ emitChanges(
1358
+ items.map((item) => ({
1359
+ row: item.row,
1360
+ rowId: item.rowId,
1361
+ columnId: item.column.id,
1362
+ value: item.value,
1363
+ previous: item.previous,
1364
+ source,
1365
+ })),
1366
+ );
1367
+ const deltas: CellDelta[] = items.map((item) => ({
1368
+ rowId: item.rowId,
1369
+ columnId: item.column.id,
1370
+ before: item.previous,
1371
+ after: item.value,
1372
+ }));
1373
+ record({ kind: "cells", source, changes: deltas });
1374
+ };
1375
+ const replay = (command: HistoryCommand<T>, source: "undo" | "redo") => {
1376
+ if (command.kind === "cells") {
1377
+ const changes: CellChange<T>[] = [];
1378
+ for (const delta of command.changes) {
1379
+ const row = rowByKey.get(delta.rowId);
1380
+ const column = byId.get(delta.columnId);
1381
+ if (!row || !column) continue;
1382
+ changes.push({
1383
+ row,
1384
+ rowId: delta.rowId,
1385
+ columnId: delta.columnId,
1386
+ value: delta.after,
1387
+ previous: rawValue(row, column),
1388
+ source,
1389
+ });
1390
+ }
1391
+ emitChanges(changes);
1392
+ return;
1393
+ }
1394
+ const rows = [...command.rows].sort((a, b) => a.index - b.index);
1395
+ onRowsChange?.({ type: command.kind === "rows-add" ? "add" : "remove", rows, source });
1396
+ };
1397
+ const undo = () => {
1398
+ const step = undoCommand(historyRef.current);
1399
+ if (!step) return;
1400
+ syncHistory(step.state);
1401
+ replay(step.apply, "undo");
1402
+ setAnnouncement(labels.undone(commandSize(step.apply)));
1403
+ };
1404
+ const redo = () => {
1405
+ const step = redoCommand(historyRef.current);
1406
+ if (!step) return;
1407
+ syncHistory(step.state);
1408
+ replay(step.apply, "redo");
1409
+ setAnnouncement(labels.redone(commandSize(step.apply)));
1410
+ };
1411
+
1412
+ // ---- row tools ---------------------------------------------------------------------------
1413
+ const addRow = () => {
1414
+ if (!createRow || !onRowsChange) return;
1415
+ const row = createRow();
1416
+ const index = dataProp.length;
1417
+ const ref: RowRef<T> = {
1418
+ row,
1419
+ rowId: getRowIdProp ? getRowIdProp(row, index) : String(index),
1420
+ index,
1421
+ };
1422
+ onRowsChange({ type: "add", rows: [ref], source: "edit" });
1423
+ record({ kind: "rows-add", rows: [ref] });
1424
+ };
1425
+ const removeSelectedRows = () => {
1426
+ if (!onRowsChange || selection.length === 0) return;
1427
+ const positions = new Map<T, number>();
1428
+ dataProp.forEach((row, index) => {
1429
+ positions.set(row, index);
1430
+ });
1431
+ const rows: RowRef<T>[] = [];
1432
+ for (const key of selection) {
1433
+ const row = rowByKey.get(key);
1434
+ const index = row === undefined ? undefined : positions.get(row);
1435
+ if (row !== undefined && index !== undefined) rows.push({ row, rowId: key, index });
1436
+ }
1437
+ rows.sort((a, b) => a.index - b.index);
1438
+ if (rows.length === 0) return;
1439
+ onRowsChange({ type: "remove", rows, source: "edit" });
1440
+ record({ kind: "rows-remove", rows });
1441
+ setSelection([]);
1442
+ };
1443
+
1444
+ // ---- paste and fill ----------------------------------------------------------------------
1445
+ const onPaste = (event: ClipboardEvent<HTMLTableElement>) => {
1446
+ const target = event.target as HTMLElement;
1447
+ if (
1448
+ !canWrite ||
1449
+ editingRef.current ||
1450
+ target.closest("input,textarea,select,[data-detail-content]")
1451
+ )
1452
+ return;
1453
+ const text = event.clipboardData.getData("text/plain");
1454
+ if (!text) return;
1455
+ event.preventDefault();
1456
+ const bounds = cellRange ? rangeBounds(cellRange) : null;
1457
+ const origin = bounds ? { r: bounds.r0, c: bounds.c0 } : { r: activeR, c: activeC };
1458
+ const items: CellEdit<T>[] = [];
1459
+ for (const cellTarget of planPaste(parseTSV(text), origin, { maxR, maxC }, bounds)) {
1460
+ const cell = cellAt(cellTarget.r, cellTarget.c);
1461
+ if (!cell || !canEdit(cell.column, cell.row)) continue;
1462
+ const result = coerceDraft(cell.column, cell.row, cellTarget.text, true);
1463
+ const previous = rawValue(cell.row, cell.column);
1464
+ if ("error" in result || sameCellValue(result.value, previous)) continue;
1465
+ items.push({
1466
+ row: cell.row,
1467
+ rowId: keyOf(cell.row),
1468
+ column: cell.column,
1469
+ value: result.value,
1470
+ previous,
1471
+ });
1472
+ }
1473
+ commitChanges(items, "paste");
1474
+ if (items.length > 0) setAnnouncement(labels.pasted(items.length));
1475
+ };
1476
+ /** Edit that writes `source` into `to`; formulas shift their relative references when copied from `from`. */
1477
+ const fillItem = (
1478
+ from: { row: T; column: DataGridColumn<T> },
1479
+ to: { row: T; column: DataGridColumn<T> },
1480
+ source: CellValue,
1481
+ shift: boolean,
1482
+ ): CellEdit<T> | null => {
1483
+ let value = source;
1484
+ if (shift && sourceIndex && isFormulaText(value)) {
1485
+ const dr = (sourceIndex.rows.get(to.row) ?? 0) - (sourceIndex.rows.get(from.row) ?? 0);
1486
+ const dc =
1487
+ (sourceIndex.cols.get(to.column.id) ?? 0) - (sourceIndex.cols.get(from.column.id) ?? 0);
1488
+ value = shiftFormula(value, dr, dc);
1489
+ }
1490
+ const result =
1491
+ typeof value === "string" ? coerceDraft(to.column, to.row, value, true) : { value };
1492
+ const previous = rawValue(to.row, to.column);
1493
+ if ("error" in result || sameCellValue(result.value, previous)) return null;
1494
+ return {
1495
+ row: to.row,
1496
+ rowId: keyOf(to.row),
1497
+ column: to.column,
1498
+ value: result.value,
1499
+ previous,
1500
+ };
1501
+ };
1502
+ /** Fills what the handle dragged over: series continue, everything else is copied (one history entry). */
1503
+ const fillByHandle = (base: FillBounds, extent: HandleExtent, copyOnly: boolean) => {
1504
+ const items: CellEdit<T>[] = [];
1505
+ for (const lane of fillLanes(base, extent)) {
1506
+ const sources = lane.source.map((pos) => cellAt(pos.r, pos.c));
1507
+ if (sources.some((cell) => !cell)) continue;
1508
+ const values = sources.map((cell) => {
1509
+ const known = cell as { row: T; column: DataGridColumn<T> };
1510
+ return rawValue(known.row, known.column);
1511
+ });
1512
+ const plan = copyOnly
1513
+ ? copyFill(lane.targets.length, values.length, isBackward(extent.direction))
1514
+ : seriesFill(values, lane.targets.length, {
1515
+ backward: isBackward(extent.direction),
1516
+ dateStep: fillSeriesOptions?.dateStep,
1517
+ });
1518
+ lane.targets.forEach((pos, i) => {
1519
+ const to = cellAt(pos.r, pos.c);
1520
+ const step = plan[i];
1521
+ if (!to || !step || !canEdit(to.column, to.row)) return;
1522
+ const item =
1523
+ "from" in step
1524
+ ? fillItem(
1525
+ sources[step.from] as { row: T; column: DataGridColumn<T> },
1526
+ to,
1527
+ values[step.from] as CellValue,
1528
+ true,
1529
+ )
1530
+ : fillItem(to, to, step.value, false);
1531
+ if (item) items.push(item);
1532
+ });
1533
+ }
1534
+ commitChanges(items, "fill");
1535
+ setCellRange({
1536
+ anchor: { r: extent.bounds.r0, c: extent.bounds.c0 },
1537
+ focus: { r: extent.bounds.r1, c: extent.bounds.c1 },
1538
+ });
1539
+ if (items.length > 0) setAnnouncement(labels.filled(items.length));
1540
+ };
1541
+ const onFillHandleDown = (event: ReactPointerEvent<HTMLElement>) => {
1542
+ if (event.button !== 0 || !cellRange || !canWrite) return;
1543
+ event.preventDefault();
1544
+ event.stopPropagation();
1545
+ const base = rangeBounds(cellRange);
1546
+ let extent: HandleExtent | null = null;
1547
+ let cancelled = false;
1548
+ let pointer = { x: event.clientX, y: event.clientY };
1549
+ let frame = 0;
1550
+ /** Viewport the pointer can hit cells in: the scroller below its sticky header. */
1551
+ const viewport = () => {
1552
+ const scroller = scrollerRef.current;
1553
+ if (!scroller) return null;
1554
+ const box = scroller.getBoundingClientRect();
1555
+ let head = box.top;
1556
+ for (const th of scroller.querySelectorAll("thead th")) {
1557
+ head = Math.max(head, th.getBoundingClientRect().bottom);
1558
+ }
1559
+ return { left: box.left, right: box.right, top: head, bottom: box.bottom };
1560
+ };
1561
+ const update = () => {
1562
+ const view = viewport();
1563
+ if (!view) return;
1564
+ const x = Math.min(Math.max(pointer.x, view.left + 2), view.right - 2);
1565
+ const y = Math.min(Math.max(pointer.y, view.top + 2), view.bottom - 2);
1566
+ const cell = document.elementFromPoint(x, y)?.closest<HTMLElement>("td[data-r]");
1567
+ if (!cell || !gridRef.current?.contains(cell)) return;
1568
+ extent = handleExtent(
1569
+ base,
1570
+ { r: Number(cell.dataset.r), c: Number(cell.dataset.c) },
1571
+ { maxR, maxC, minR: 1, minC: 0 },
1572
+ );
1573
+ setFillPreview(extent?.bounds ?? null);
1574
+ };
1575
+ const autoScroll = () => {
1576
+ frame = requestAnimationFrame(autoScroll);
1577
+ const view = viewport();
1578
+ const scroller = scrollerRef.current;
1579
+ if (!view || !scroller) return;
1580
+ const dx = edgeScrollStep(pointer.x, view.left, view.right);
1581
+ const dy = edgeScrollStep(pointer.y, view.top, view.bottom);
1582
+ if (dx === 0 && dy === 0) return;
1583
+ const before = { left: scroller.scrollLeft, top: scroller.scrollTop };
1584
+ scroller.scrollLeft += dx;
1585
+ scroller.scrollTop += dy;
1586
+ if (scroller.scrollLeft !== before.left || scroller.scrollTop !== before.top) update();
1587
+ };
1588
+ const move = (moveEvent: PointerEvent) => {
1589
+ pointer = { x: moveEvent.clientX, y: moveEvent.clientY };
1590
+ update();
1591
+ };
1592
+ const key = (keyEvent: globalThis.KeyboardEvent) => {
1593
+ if (keyEvent.key !== "Escape") return;
1594
+ cancelled = true;
1595
+ finish(false);
1596
+ };
1597
+ const finish = (apply: boolean, copyOnly = false) => {
1598
+ window.removeEventListener("pointermove", move);
1599
+ window.removeEventListener("pointerup", up);
1600
+ window.removeEventListener("pointercancel", cancel);
1601
+ window.removeEventListener("keydown", key);
1602
+ cancelAnimationFrame(frame);
1603
+ setFillPreview(null);
1604
+ if (apply && !cancelled && extent) fillByHandle(base, extent, copyOnly);
1605
+ };
1606
+ const up = (upEvent: PointerEvent) => finish(true, upEvent.ctrlKey || upEvent.metaKey);
1607
+ const cancel = () => finish(false);
1608
+ window.addEventListener("pointermove", move);
1609
+ window.addEventListener("pointerup", up);
1610
+ window.addEventListener("pointercancel", cancel);
1611
+ window.addEventListener("keydown", key);
1612
+ frame = requestAnimationFrame(autoScroll);
1613
+ };
1614
+ const fillRange = (direction: FillDirection) => {
1615
+ if (!canWrite) return;
1616
+ const bounds = cellRange
1617
+ ? rangeBounds(cellRange)
1618
+ : direction === "down"
1619
+ ? { r0: activeR - 1, r1: activeR, c0: activeC, c1: activeC }
1620
+ : { r0: activeR, r1: activeR, c0: activeC - 1, c1: activeC };
1621
+ if (bounds.r0 < 1 || bounds.c0 < 0) return;
1622
+ const items: CellEdit<T>[] = [];
1623
+ for (const step of planFill(bounds, direction)) {
1624
+ const from = cellAt(step.from.r, step.from.c);
1625
+ const to = cellAt(step.r, step.c);
1626
+ if (!from || !to || !canEdit(to.column, to.row)) continue;
1627
+ const item = fillItem(from, to, rawValue(from.row, from.column), true);
1628
+ if (item) items.push(item);
1629
+ }
1630
+ commitChanges(items, "fill");
1631
+ if (items.length > 0) setAnnouncement(labels.filled(items.length));
1632
+ };
1633
+
1634
+ const startEdit = (key: string, column: DataGridColumn<T>, typed?: string) => {
1635
+ const row = rowByKey.get(key);
1636
+ if (!row || !canEdit(column, row)) return false;
1637
+ const value = rawValue(row, column);
1638
+ setEditing({
1639
+ key,
1640
+ columnId: column.id,
1641
+ draft: typed ?? (value === null || value === undefined ? "" : String(value)),
1642
+ caretEnd: typed !== undefined,
1643
+ });
1644
+ return true;
1645
+ };
1646
+ /**
1647
+ * Commits the open edit. With `strict` (Enter, Tab) an invalid value keeps the editor open with
1648
+ * its message and returns false; on blur an invalid value is discarded.
1649
+ */
1650
+ const commitEdit = (strict = false): boolean => {
1651
+ const current = editingRef.current;
1652
+ if (!current) return true;
1653
+ const row = rowByKey.get(current.key);
1654
+ const column = byId.get(current.columnId);
1655
+ const close = () => {
1656
+ editingRef.current = null;
1657
+ setEditing(null);
1658
+ };
1659
+ if (!row || !column) {
1660
+ close();
1661
+ return true;
1662
+ }
1663
+ const previous = rawValue(row, column);
1664
+ const result = coerceDraft(column, row, current.draft);
1665
+ if ("error" in result) {
1666
+ if (!strict) {
1667
+ close();
1668
+ return true;
1669
+ }
1670
+ const next = { ...current, error: result.error };
1671
+ editingRef.current = next;
1672
+ setEditing(next);
1673
+ return false;
1674
+ }
1675
+ close();
1676
+ if (!sameCellValue(result.value, previous)) {
1677
+ commitChanges([{ row, rowId: current.key, column, value: result.value, previous }], "edit");
1678
+ }
1679
+ return true;
1680
+ };
1681
+ const cancelEdit = () => {
1682
+ editingRef.current = null;
1683
+ setEditing(null);
1684
+ };
1685
+ const nextEditable = (r: number, c: number, step: 1 | -1): { r: number; c: number } | null => {
1686
+ let row = r;
1687
+ let col = c + step;
1688
+ while (row >= 1 && row <= maxR) {
1689
+ const display = bodyRows[row - 1];
1690
+ if (display?.kind === "data") {
1691
+ while (col >= 0 && col <= maxC) {
1692
+ const entry = renderColumns[col];
1693
+ if (entry?.kind === "data" && canEdit(entry.column, display.row))
1694
+ return { r: row, c: col };
1695
+ col += step;
1696
+ }
1697
+ }
1698
+ row += step;
1699
+ col = step > 0 ? 0 : maxC;
1700
+ }
1701
+ return null;
1702
+ };
1703
+ const onEditorKey = (event: KeyboardEvent<HTMLElement>, r: number, c: number) => {
1704
+ event.stopPropagation();
1705
+ if (event.key === "Escape") {
1706
+ event.preventDefault();
1707
+ cancelEdit();
1708
+ focusCell(r, c);
1709
+ } else if (event.key === "Enter") {
1710
+ event.preventDefault();
1711
+ if (!commitEdit(true)) return;
1712
+ focusCell(Math.min(r + 1, maxR), c);
1713
+ } else if (event.key === "Tab") {
1714
+ event.preventDefault();
1715
+ if (!commitEdit(true)) return;
1716
+ const next = nextEditable(r, c, event.shiftKey ? -1 : 1);
1717
+ if (!next) {
1718
+ focusCell(r, c);
1719
+ return;
1720
+ }
1721
+ const target = bodyRows[next.r - 1];
1722
+ const entry = renderColumns[next.c];
1723
+ focusCell(next.r, next.c, false);
1724
+ if (target?.kind === "data" && entry?.kind === "data") startEdit(target.key, entry.column);
1725
+ }
1726
+ };
1727
+
1728
+ // ---- keyboard ----------------------------------------------------------------------------
1729
+ const onGridKeyDown = (event: KeyboardEvent<HTMLTableElement>) => {
1730
+ const target = event.target as HTMLElement;
1731
+ if (target.closest("[data-detail-content]")) return;
1732
+ const cell = target.closest<HTMLElement>("[data-r]");
1733
+ if (!cell || !event.currentTarget.contains(cell)) return;
1734
+ const r = Number(cell.dataset.r);
1735
+ const start = Number(cell.dataset.c);
1736
+ const spanEnd = Number(cell.dataset.cEnd ?? start);
1737
+ const c = activeR === r && activeC >= start && activeC <= spanEnd ? activeC : start;
1738
+ const inner = target !== cell;
1739
+ if (inner && (event.key === "Enter" || event.key === " ")) return;
1740
+ const entry = renderColumns[c];
1741
+ const row = r > 0 ? bodyRows[r - 1] : undefined;
1742
+ const handled = () => event.preventDefault();
1743
+ const mod = event.ctrlKey || event.metaKey;
1744
+
1745
+ if (mod && !event.altKey && !target.closest("input,textarea,select")) {
1746
+ const letter = event.key.toLowerCase();
1747
+ if (historyEnabled && (letter === "z" || (letter === "y" && !event.shiftKey))) {
1748
+ handled();
1749
+ if (letter === "y" || event.shiftKey) redo();
1750
+ else undo();
1751
+ return;
1752
+ }
1753
+ if (r > 0 && canWrite && !event.shiftKey && (letter === "d" || letter === "r")) {
1754
+ handled();
1755
+ fillRange(letter === "d" ? "down" : "right");
1756
+ return;
1757
+ }
1758
+ }
1759
+
1760
+ if (r === 0 && entry?.kind === "data") {
1761
+ const id = entry.column.id;
1762
+ if ((event.key === "ArrowLeft" || event.key === "ArrowRight") && event.shiftKey) {
1763
+ const delta = event.key === "ArrowLeft" ? -1 : 1;
1764
+ handled();
1765
+ if (mod) {
1766
+ if (entry.column.reorderable !== false && moveColumn(id, delta)) focusCell(0, c + delta);
1767
+ } else if (entry.column.resizable !== false) {
1768
+ grid.setWidth(id, (shownWidths[id] ?? 160) + delta * 16);
1769
+ }
1770
+ return;
1771
+ }
1772
+ if (event.key === "ArrowDown" && event.altKey) {
1773
+ handled();
1774
+ setMenuColumn(id);
1775
+ return;
1776
+ }
1777
+ if (event.key === "Enter" || event.key === " ") {
1778
+ handled();
1779
+ if (entry.column.sortable !== false) applySort(toggleSort(sort, id, event.shiftKey));
1780
+ return;
1781
+ }
1782
+ }
1783
+
1784
+ if (r > 0 && rangeSelection && !event.altKey) {
1785
+ const step = ARROW_STEPS[event.key];
1786
+ if (step && event.shiftKey) {
1787
+ handled();
1788
+ const from = { r, c: Math.max(c, firstDataIndex) };
1789
+ const base = cellRange ?? { anchor: from, focus: from };
1790
+ const next = extendRange(base, step.dr * (mod ? maxR : 1), step.dc * (mod ? maxC : 1), {
1791
+ minR: 1,
1792
+ maxR,
1793
+ minC: Math.max(0, firstDataIndex),
1794
+ maxC,
1795
+ });
1796
+ setCellRange(next);
1797
+ focusCell(next.focus.r, next.focus.c);
1798
+ return;
1799
+ }
1800
+ if (mod && event.key.toLowerCase() === "c" && cellRange) {
1801
+ handled();
1802
+ copyRange();
1803
+ return;
1804
+ }
1805
+ if (mod && event.key.toLowerCase() === "a" && !selectable) {
1806
+ handled();
1807
+ setCellRange({
1808
+ anchor: { r: 1, c: Math.max(0, firstDataIndex) },
1809
+ focus: { r: maxR, c: maxC },
1810
+ });
1811
+ return;
1812
+ }
1813
+ if (
1814
+ cellRange &&
1815
+ (step || ["Home", "End", "PageUp", "PageDown", "Escape"].includes(event.key))
1816
+ ) {
1817
+ setCellRange(null);
1818
+ }
1819
+ }
1820
+ if (r > 0 && row?.kind === "data" && event.altKey && !mod) {
1821
+ const vertical = event.key === "ArrowUp" ? -1 : event.key === "ArrowDown" ? 1 : 0;
1822
+ if (vertical !== 0 && event.shiftKey && pinningEnabled) {
1823
+ handled();
1824
+ pinRow(row.key, vertical < 0 ? "top" : "bottom");
1825
+ return;
1826
+ }
1827
+ if (
1828
+ vertical !== 0 &&
1829
+ !event.shiftKey &&
1830
+ reorderAllowed &&
1831
+ !pinnedSideOf(pinnedRowIds, row.key)
1832
+ ) {
1833
+ handled();
1834
+ if (reorderRow(row.key, { delta: vertical })) focusCell(r + vertical, c);
1835
+ return;
1836
+ }
1837
+ if (event.key === "ArrowRight" || event.key === "ArrowLeft") {
1838
+ const expandable = detailEnabled || !!row.tree?.hasChildren;
1839
+ if (expandable) {
1840
+ handled();
1841
+ setExpandedRow(row.key, event.key === "ArrowRight");
1842
+ return;
1843
+ }
1844
+ }
1845
+ }
1846
+
1847
+ switch (event.key) {
1848
+ case "ArrowRight":
1849
+ handled();
1850
+ focusCell(r, spanEnd > start ? spanEnd + 1 : c + 1);
1851
+ return;
1852
+ case "ArrowLeft":
1853
+ handled();
1854
+ focusCell(r, spanEnd > start ? start - 1 : c - 1);
1855
+ return;
1856
+ case "ArrowDown":
1857
+ handled();
1858
+ focusCell(r + 1, c);
1859
+ return;
1860
+ case "ArrowUp":
1861
+ handled();
1862
+ focusCell(r - 1, c);
1863
+ return;
1864
+ case "Home":
1865
+ handled();
1866
+ if (mod && scrollerRef.current) scrollerRef.current.scrollTop = 0;
1867
+ focusCell(mod ? 0 : r, 0);
1868
+ return;
1869
+ case "End":
1870
+ handled();
1871
+ focusCell(mod ? maxR : r, maxC);
1872
+ return;
1873
+ case "PageDown":
1874
+ case "PageUp": {
1875
+ handled();
1876
+ const step = paged ? 10 : pageStep(rowHeight, bodyViewport);
1877
+ focusCell(Math.max(r === 0 ? 0 : 1, r + (event.key === "PageDown" ? step : -step)), c);
1878
+ return;
1879
+ }
1880
+ }
1881
+
1882
+ if (mod && event.key.toLowerCase() === "a" && selectable) {
1883
+ handled();
1884
+ toggleAll();
1885
+ return;
1886
+ }
1887
+ if (r === 0 && entry?.kind === "select" && (event.key === " " || event.key === "Enter")) {
1888
+ handled();
1889
+ toggleAll();
1890
+ return;
1891
+ }
1892
+ if (!row || row.kind === "placeholder" || row.kind === "detail") return;
1893
+ if (row.kind === "group") {
1894
+ if (event.key === "Enter" || event.key === " ") {
1895
+ handled();
1896
+ toggleGroupRow(row.key);
1897
+ } else if (event.key === "ArrowRight" && row.collapsed) {
1898
+ handled();
1899
+ toggleGroupRow(row.key);
1900
+ }
1901
+ return;
1902
+ }
1903
+ if ((event.key === "Enter" || event.key === " ") && entry?.kind === "tools") {
1904
+ handled();
1905
+ if (detailEnabled) setExpandedRow(row.key);
1906
+ return;
1907
+ }
1908
+ if (event.key === " " && selectable) {
1909
+ handled();
1910
+ toggleRow(row.key, event.shiftKey);
1911
+ return;
1912
+ }
1913
+ if (formulasOn && event.key === "=" && !mod && !inner && entry?.kind === "data") {
1914
+ if (startEdit(row.key, entry.column, "=")) handled();
1915
+ return;
1916
+ }
1917
+ if ((event.key === "Enter" || event.key === "F2") && entry?.kind === "data") {
1918
+ if (startEdit(row.key, entry.column)) handled();
1919
+ else if (event.key === "Enter" && selectable && entry.kind === "data") return;
1920
+ }
1921
+ if (event.key === "Enter" && entry?.kind === "select") {
1922
+ handled();
1923
+ toggleRow(row.key, event.shiftKey);
1924
+ }
1925
+ };
1926
+
1927
+ const onGridFocus = (event: FocusEvent<HTMLTableElement>) => {
1928
+ if ((event.target as HTMLElement).closest("[data-detail-content]")) return;
1929
+ const cell = (event.target as HTMLElement).closest<HTMLElement>("[data-r]");
1930
+ if (!cell || !event.currentTarget.contains(cell)) return;
1931
+ const r = Number(cell.dataset.r);
1932
+ const c = Number(cell.dataset.c);
1933
+ const end = Number(cell.dataset.cEnd ?? c);
1934
+ if (r === active.r && active.c >= c && active.c <= end) return;
1935
+ setActive({ r, c });
1936
+ };
1937
+
1938
+ // ---- pointer: resize and reorder ---------------------------------------------------------
1939
+ const onResizeStart = (event: ReactPointerEvent<HTMLElement>, column: DataGridColumn<T>) => {
1940
+ event.preventDefault();
1941
+ event.stopPropagation();
1942
+ resizing.current = true;
1943
+ const handle = event.currentTarget;
1944
+ const startX = event.clientX;
1945
+ const startWidth = shownWidths[column.id] ?? 160;
1946
+ const direction = zoneOf.get(column.id) === "right" ? -1 : 1;
1947
+ handle.setPointerCapture(event.pointerId);
1948
+ const move = (moveEvent: PointerEvent) =>
1949
+ grid.setWidth(column.id, startWidth + direction * (moveEvent.clientX - startX));
1950
+ const end = () => {
1951
+ resizing.current = false;
1952
+ handle.removeEventListener("pointermove", move);
1953
+ handle.removeEventListener("pointerup", end);
1954
+ handle.removeEventListener("pointercancel", end);
1955
+ };
1956
+ handle.addEventListener("pointermove", move);
1957
+ handle.addEventListener("pointerup", end);
1958
+ handle.addEventListener("pointercancel", end);
1959
+ };
1960
+
1961
+ const onDragStart = (event: DragEvent<HTMLElement>, id: string) => {
1962
+ if (resizing.current) {
1963
+ event.preventDefault();
1964
+ return;
1965
+ }
1966
+ dragId.current = id;
1967
+ event.dataTransfer.effectAllowed = "move";
1968
+ event.dataTransfer.setData("text/plain", id);
1969
+ };
1970
+ const onDragOver = (event: DragEvent<HTMLElement>, id: string) => {
1971
+ const source = dragId.current;
1972
+ if (!source || source === id || zoneOf.get(source) !== zoneOf.get(id)) return;
1973
+ event.preventDefault();
1974
+ const rect = event.currentTarget.getBoundingClientRect();
1975
+ const place = event.clientX < rect.left + rect.width / 2 ? "before" : "after";
1976
+ setDropTarget((previous) =>
1977
+ previous?.id === id && previous.place === place ? previous : { id, place },
1978
+ );
1979
+ };
1980
+ const onDrop = (event: DragEvent<HTMLElement>, id: string) => {
1981
+ event.preventDefault();
1982
+ const source = dragId.current;
1983
+ const rect = event.currentTarget.getBoundingClientRect();
1984
+ const place = event.clientX < rect.left + rect.width / 2 ? "before" : "after";
1985
+ if (source && source !== id && zoneOf.get(source) === zoneOf.get(id)) {
1986
+ grid.moveTo(source, id, place);
1987
+ }
1988
+ dragId.current = null;
1989
+ setDropTarget(null);
1990
+ };
1991
+ const onDragEnd = () => {
1992
+ dragId.current = null;
1993
+ setDropTarget(null);
1994
+ };
1995
+
1996
+ const onRowDragStart = (event: DragEvent<HTMLElement>, key: string) => {
1997
+ if (!reorderAllowed) {
1998
+ event.preventDefault();
1999
+ return;
2000
+ }
2001
+ rowDrag.current = key;
2002
+ event.dataTransfer.effectAllowed = "move";
2003
+ event.dataTransfer.setData("text/plain", key);
2004
+ const row = event.currentTarget.closest("tr");
2005
+ if (row) event.dataTransfer.setDragImage(row, 16, rowHeight / 2);
2006
+ };
2007
+ const dropPlace = (event: DragEvent<HTMLElement>) => {
2008
+ const rect = event.currentTarget.getBoundingClientRect();
2009
+ return event.clientY < rect.top + rect.height / 2 ? "before" : "after";
2010
+ };
2011
+ const onRowDragOver = (event: DragEvent<HTMLElement>, key: string) => {
2012
+ const source = rowDrag.current;
2013
+ if (!source || source === key) return;
2014
+ event.preventDefault();
2015
+ const place = dropPlace(event);
2016
+ setDropRow((previous) =>
2017
+ previous?.key === key && previous.place === place ? previous : { key, place },
2018
+ );
2019
+ };
2020
+ const onRowDrop = (event: DragEvent<HTMLElement>, key: string) => {
2021
+ event.preventDefault();
2022
+ const source = rowDrag.current;
2023
+ if (source && source !== key) reorderRow(source, { id: key, place: dropPlace(event) });
2024
+ rowDrag.current = null;
2025
+ setDropRow(null);
2026
+ };
2027
+ const onRowDragEnd = () => {
2028
+ rowDrag.current = null;
2029
+ setDropRow(null);
2030
+ };
2031
+
2032
+ // ---- rendering helpers -------------------------------------------------------------------
2033
+ const pinOf = (entry: RenderColumn<T>): PinPosition | undefined => positions[idOfColumn(entry)];
2034
+ const pinStyle = (position: PinPosition | undefined): CSSProperties | undefined =>
2035
+ position ? { [position.side]: position.offset } : undefined;
2036
+ const pinShadow = (position: PinPosition | undefined) =>
2037
+ position?.edge &&
2038
+ (position.side === "left" ? scrolled.start : scrolled.end) &&
2039
+ (position.side === "left" ? "shadow-pinned-start" : "shadow-pinned-end");
2040
+ const cellBase =
2041
+ "overflow-hidden border-b-divider px-3 outline-none focus-visible:outline-2 focus-visible:outline-accent focus-visible:-outline-offset-2 @max-sm/grid:px-2";
2042
+ const tabIndexOf = (r: number, c: number) => (tabbable.r === r && tabbable.c === c ? 0 : -1);
2043
+ const firstDataIndex = renderColumns.findIndex((entry) => entry.kind === "data");
2044
+
2045
+ const cellLetter = (id: string) => columnLabel(sourceIndex?.cols.get(id) ?? 0);
2046
+ const sortOf = (id: string) => {
2047
+ const index = sort.findIndex((rule) => rule.id === id);
2048
+ return index === -1 ? null : { index, direction: (sort[index] as SortRule).direction };
2049
+ };
2050
+
2051
+ const renderHeader = (entry: RenderColumn<T>, c: number) => {
2052
+ const position = pinOf(entry);
2053
+ const common = {
2054
+ "data-r": 0,
2055
+ "data-c": c,
2056
+ tabIndex: tabIndexOf(0, c),
2057
+ style: { ...pinStyle(position), top: headerTop, height: headerHeight },
2058
+ };
2059
+ const sticky = cn("sticky bg-surface", position ? "z-3" : "z-2", pinShadow(position));
2060
+ if (entry.kind === "tools") {
2061
+ return (
2062
+ <th key={TOOLS_ID} className={cn(cellBase, sticky, "px-0")} {...common}>
2063
+ <span className="sr-only">{labels.rowActions}</span>
2064
+ </th>
2065
+ );
2066
+ }
2067
+ if (entry.kind === "select") {
2068
+ const all = allKeys.length > 0 && selectedInView === allKeys.length;
2069
+ return (
2070
+ <th key={SELECT_ID} className={cn(cellBase, sticky, "px-0 text-center")} {...common}>
2071
+ <input
2072
+ type="checkbox"
2073
+ tabIndex={-1}
2074
+ aria-label={labels.selectAll}
2075
+ className="size-4 align-middle accent-accent"
2076
+ checked={all}
2077
+ ref={(node) => {
2078
+ if (node) node.indeterminate = selectedInView > 0 && !all;
2079
+ }}
2080
+ onChange={toggleAll}
2081
+ />
2082
+ </th>
2083
+ );
2084
+ }
2085
+ const { column } = entry;
2086
+ const align = columnAlign(column);
2087
+ const state = sortOf(column.id);
2088
+ const sortable = column.sortable !== false;
2089
+ const reorderable = column.reorderable !== false;
2090
+ const zone = zoneOf.get(column.id) ?? "center";
2091
+ const neighbourLeft = neighbourOf(column.id, -1);
2092
+ const neighbourRight = neighbourOf(column.id, 1);
2093
+ const drop = dropTarget?.id === column.id ? dropTarget.place : null;
2094
+ return (
2095
+ <th
2096
+ key={column.id}
2097
+ aria-sort={
2098
+ state?.index === 0 ? (state.direction === "asc" ? "ascending" : "descending") : undefined
2099
+ }
2100
+ draggable={reorderable && !editing}
2101
+ onDragStart={(event) => onDragStart(event, column.id)}
2102
+ onDragOver={(event) => onDragOver(event, column.id)}
2103
+ onDragLeave={() => setDropTarget(null)}
2104
+ onDrop={(event) => onDrop(event, column.id)}
2105
+ onDragEnd={onDragEnd}
2106
+ onClick={(event) => {
2107
+ if ((event.target as HTMLElement).closest("button,[data-resize]")) return;
2108
+ if (sortable) applySort(toggleSort(sort, column.id, event.shiftKey));
2109
+ }}
2110
+ className={cn(
2111
+ cellBase,
2112
+ sticky,
2113
+ "select-none whitespace-nowrap font-[650] text-[11px] text-ink-3 uppercase tracking-[.07em]",
2114
+ sortable && "cursor-pointer hover:text-ink",
2115
+ drop === "before" && "shadow-[inset_2px_0_0_var(--accent)]",
2116
+ drop === "after" && "shadow-[inset_-2px_0_0_var(--accent)]",
2117
+ )}
2118
+ data-zone={zone}
2119
+ {...common}
2120
+ >
2121
+ <div
2122
+ className={cn("flex min-w-0 items-center gap-1", align === "end" && "flex-row-reverse")}
2123
+ >
2124
+ <span className={cn("flex min-w-0 flex-1 items-center gap-1", justifyClass[align])}>
2125
+ {formulasOn ? (
2126
+ <span
2127
+ aria-hidden="true"
2128
+ className="shrink-0 rounded-xs bg-surface-2 px-1 font-[650] text-[10px] text-ink-3 normal-case tracking-normal"
2129
+ >
2130
+ {cellLetter(column.id)}
2131
+ </span>
2132
+ ) : null}
2133
+ <span className="truncate" title={column.header}>
2134
+ {column.header}
2135
+ </span>
2136
+ {state ? (
2137
+ <span className="inline-flex shrink-0 items-center text-accent-ink">
2138
+ {state.direction === "asc" ? (
2139
+ <ArrowUp aria-hidden="true" className="size-3" />
2140
+ ) : (
2141
+ <ArrowDown aria-hidden="true" className="size-3" />
2142
+ )}
2143
+ {sort.length > 1 ? (
2144
+ <span className="text-[10px] tabular-nums">{state.index + 1}</span>
2145
+ ) : null}
2146
+ {state.index > 0 ? (
2147
+ <span className="sr-only">
2148
+ {state.direction === "asc" ? labels.sortAsc : labels.sortDesc}
2149
+ </span>
2150
+ ) : null}
2151
+ </span>
2152
+ ) : null}
2153
+ </span>
2154
+ <ColumnMenu
2155
+ column={column}
2156
+ labels={labels}
2157
+ open={menuColumn === column.id}
2158
+ onOpenChange={(open) => setMenuColumn(open ? column.id : null)}
2159
+ onCloseFocus={() => focusCell(0, c)}
2160
+ sortDirection={state?.direction ?? null}
2161
+ filter={filters[column.id]}
2162
+ values={() => distinctValues(data, column)}
2163
+ pinned={zone === "center" ? null : zone}
2164
+ grouped={groupBy.includes(column.id)}
2165
+ canMoveLeft={!!neighbourLeft}
2166
+ canMoveRight={!!neighbourRight}
2167
+ canHide={column.hideable !== false && dataColumns.length > 1}
2168
+ onSort={(direction) => applySort(direction ? [{ id: column.id, direction }] : [])}
2169
+ onFilter={(filter) => setFilter(column.id, filter)}
2170
+ onPin={(side) => grid.pin(column.id, side)}
2171
+ onGroup={(on) => setGroup(column.id, on)}
2172
+ onMove={(delta) => moveColumn(column.id, delta)}
2173
+ onResize={(delta) =>
2174
+ delta === null
2175
+ ? grid.resetWidth(column.id)
2176
+ : grid.setWidth(column.id, (shownWidths[column.id] ?? 160) + delta)
2177
+ }
2178
+ onHide={() => {
2179
+ setMenuColumn(null);
2180
+ grid.setHidden(column.id, true);
2181
+ }}
2182
+ />
2183
+ </div>
2184
+ {column.resizable !== false ? (
2185
+ <span
2186
+ aria-hidden="true"
2187
+ data-resize=""
2188
+ onPointerDown={(event) => onResizeStart(event, column)}
2189
+ onDoubleClick={() => grid.resetWidth(column.id)}
2190
+ className={cn(
2191
+ "absolute top-0 z-1 h-full w-2 cursor-col-resize touch-none after:absolute after:inset-y-2 after:left-1/2 after:w-px after:bg-line hover:after:w-0.5 hover:after:bg-accent",
2192
+ zone === "right" ? "left-0" : "right-0",
2193
+ )}
2194
+ />
2195
+ ) : null}
2196
+ </th>
2197
+ );
2198
+ };
2199
+
2200
+ const renderValue = (row: T, key: string, column: DataGridColumn<T>) => {
2201
+ const value = getCellValue(row, column);
2202
+ const formatted = formatCellValue(value, column);
2203
+ if (column.cell) return column.cell({ row, rowId: key, value, formatted });
2204
+ if (formulasOn && isErrorText(value) && isFormulaText(rawValue(row, column))) {
2205
+ return (
2206
+ <span
2207
+ className="block truncate font-[650] text-danger"
2208
+ title={`${labels.formulaError}: ${value}`}
2209
+ >
2210
+ {value}
2211
+ </span>
2212
+ );
2213
+ }
2214
+ if (column.type === "status" && formatted) {
2215
+ return (
2216
+ <Chip tone={column.statusTones?.[String(value)] ?? "neutral"} className="h-5.5">
2217
+ {formatted}
2218
+ </Chip>
2219
+ );
2220
+ }
2221
+ if (column.type === "sparkline" && column.sparkline) {
2222
+ const { values, kind, color, label } = column.sparkline;
2223
+ const description = typeof label === "function" ? label(row) : (label ?? column.header);
2224
+ return (
2225
+ <div className="min-w-0 py-1">
2226
+ <Sparkline
2227
+ data={values(row)}
2228
+ label={description}
2229
+ type={kind}
2230
+ color={color}
2231
+ height={24}
2232
+ animate={false}
2233
+ />
2234
+ </div>
2235
+ );
2236
+ }
2237
+ if (column.type === "boolean" && formatted) {
2238
+ return (
2239
+ <Chip tone={value === true ? "success" : "neutral"} className="h-5.5">
2240
+ {formatted}
2241
+ </Chip>
2242
+ );
2243
+ }
2244
+ return (
2245
+ <span className="block truncate" title={formatted}>
2246
+ {formatted}
2247
+ </span>
2248
+ );
2249
+ };
2250
+
2251
+ const renderEditor = (column: DataGridColumn<T>, r: number, c: number) => {
2252
+ if (!editing) return null;
2253
+ const errorId = `${helpId}-error`;
2254
+ const editorClass = cn(
2255
+ "h-[calc(100%-6px)] w-full min-w-0 rounded-xs border bg-surface px-1.5 text-[13px] text-ink outline-none",
2256
+ editing.error ? "border-danger" : "border-accent",
2257
+ );
2258
+ const common = {
2259
+ autoFocus: true,
2260
+ "aria-label": column.header,
2261
+ "aria-invalid": editing.error ? true : undefined,
2262
+ "aria-describedby": editing.error ? errorId : undefined,
2263
+ value: editing.draft,
2264
+ onBlur: () => commitEdit(),
2265
+ onKeyDown: (event: KeyboardEvent<HTMLElement>) => onEditorKey(event, r, c),
2266
+ };
2267
+ const change = (draft: string) => setEditing({ ...editing, draft, error: undefined });
2268
+ const editor =
2269
+ editorOf(column) === "select" ? (
2270
+ <select
2271
+ {...common}
2272
+ onChange={(event) => change(event.target.value)}
2273
+ className={editorClass}
2274
+ >
2275
+ {(column.options ?? []).map((option) => (
2276
+ <option key={option} value={option}>
2277
+ {option}
2278
+ </option>
2279
+ ))}
2280
+ </select>
2281
+ ) : (
2282
+ <input
2283
+ {...common}
2284
+ type="text"
2285
+ inputMode={editorOf(column) === "number" ? "decimal" : undefined}
2286
+ onFocus={(event) => {
2287
+ const input = event.currentTarget;
2288
+ if (editing.caretEnd) input.setSelectionRange(input.value.length, input.value.length);
2289
+ else input.select();
2290
+ }}
2291
+ onChange={(event) => change(event.target.value)}
2292
+ className={cn(editorClass, columnAlign(column) === "end" && "text-right")}
2293
+ />
2294
+ );
2295
+ return (
2296
+ <>
2297
+ {editor}
2298
+ {editing.error ? (
2299
+ <span
2300
+ id={errorId}
2301
+ role="alert"
2302
+ className="absolute top-full left-1 z-5 mt-0.5 flex max-w-64 items-center gap-1.5 rounded-xs border border-danger bg-surface px-2 py-1 text-[12px] text-danger shadow-overlay"
2303
+ >
2304
+ <CircleAlert aria-hidden="true" className="size-3.5 shrink-0" />
2305
+ {editing.error}
2306
+ </span>
2307
+ ) : null}
2308
+ </>
2309
+ );
2310
+ };
2311
+
2312
+ const ariaRowOf = (r: number) => groupLevels + pageOffset + r + 1;
2313
+ const dividerRow = (r: number) => (topCount > 0 && r === topCount ? "top" : null);
2314
+
2315
+ const renderGroupRow = (row: Extract<DisplayRow<T>, { kind: "group" }>, r: number) => {
2316
+ const header = byId.get(row.columnId)?.header ?? row.columnId;
2317
+ // The label spans the leading columns without aggregates so it never truncates to nothing.
2318
+ let spanEnd = Math.max(0, firstDataIndex);
2319
+ while (spanEnd + 1 < fillerIndex) {
2320
+ const next = renderColumns[spanEnd + 1];
2321
+ if (next?.kind !== "data" || next.column.aggregate) break;
2322
+ spanEnd += 1;
2323
+ }
2324
+ const labelActive = tabbable.r === r && tabbable.c <= spanEnd;
2325
+ const cells = renderColumns.map((entry, c) => {
2326
+ if (c > 0 && c <= spanEnd) return null;
2327
+ if (c === 0) {
2328
+ return (
2329
+ <td
2330
+ key="label"
2331
+ data-r={r}
2332
+ data-c={0}
2333
+ data-c-end={spanEnd}
2334
+ colSpan={spanEnd + 1}
2335
+ tabIndex={labelActive ? 0 : -1}
2336
+ role="gridcell"
2337
+ aria-expanded={!row.collapsed}
2338
+ className={cn(cellBase, "cursor-pointer overflow-visible text-[13px]")}
2339
+ >
2340
+ <span
2341
+ className="sticky left-3 flex w-fit max-w-full min-w-0 items-center gap-1.5 whitespace-nowrap font-[650] text-ink"
2342
+ style={{ paddingLeft: row.depth * 16 }}
2343
+ title={`${header}: ${row.label}`}
2344
+ >
2345
+ <ChevronDown
2346
+ aria-hidden="true"
2347
+ className={cn(
2348
+ "size-3.5 shrink-0 text-ink-3 transition-transform motion-reduce:transition-none",
2349
+ row.collapsed && "-rotate-90",
2350
+ )}
2351
+ />
2352
+ <span className="sr-only">
2353
+ {row.collapsed ? labels.expandGroup : labels.collapseGroup}:{" "}
2354
+ </span>
2355
+ <span className="shrink-0 font-normal text-ink-3">{header}:</span>
2356
+ <span className="min-w-0 truncate">{row.label}</span>
2357
+ <Chip className="h-5 shrink-0 px-2 text-[11px]">{row.count}</Chip>
2358
+ </span>
2359
+ </td>
2360
+ );
2361
+ }
2362
+ const position = pinOf(entry);
2363
+ const column = entry.kind === "data" ? entry.column : null;
2364
+ const aggregate = column?.aggregate ? (row.aggregates[column.id] ?? null) : null;
2365
+ return (
2366
+ <td
2367
+ key={idOfColumn(entry)}
2368
+ data-r={r}
2369
+ data-c={c}
2370
+ tabIndex={tabIndexOf(r, c)}
2371
+ role="gridcell"
2372
+ style={pinStyle(position)}
2373
+ className={cn(
2374
+ cellBase,
2375
+ "cursor-pointer text-[13px]",
2376
+ position && "sticky z-1 bg-surface-2",
2377
+ pinShadow(position),
2378
+ column && alignClass[columnAlign(column)],
2379
+ )}
2380
+ >
2381
+ {column?.aggregate && aggregate !== null ? (
2382
+ <span className="block truncate font-[650] text-ink tabular-nums">
2383
+ <span className="sr-only">{labels.aggregate[column.aggregate]}: </span>
2384
+ {formatAggregate(aggregate, column.aggregate, column)}
2385
+ </span>
2386
+ ) : null}
2387
+ </td>
2388
+ );
2389
+ });
2390
+ return (
2391
+ <tr
2392
+ key={row.key}
2393
+ aria-rowindex={ariaRowOf(r)}
2394
+ style={{ height: rowHeight }}
2395
+ className="group/row bg-surface-2"
2396
+ onClick={() => toggleGroupRow(row.key)}
2397
+ >
2398
+ {cells.slice(0, fillerIndex)}
2399
+ <td aria-hidden="true" className="border-b-divider" />
2400
+ {cells.slice(fillerIndex)}
2401
+ </tr>
2402
+ );
2403
+ };
2404
+
2405
+ /** Row stuck to the top or bottom of the scroller (pinned rows), with its sticky offset. */
2406
+ const rowPinInfo = (r: number) => {
2407
+ const index = r - 1;
2408
+ if (index < topCount) {
2409
+ return { side: "top" as const, offset: headerTop + headerHeight + index * rowHeight };
2410
+ }
2411
+ if (index >= bodyLength - bottomCount) {
2412
+ const fromEnd = bodyLength - 1 - index;
2413
+ return { side: "bottom" as const, offset: (footer ? rowHeight : 0) + fromEnd * rowHeight };
2414
+ }
2415
+ return null;
2416
+ };
2417
+
2418
+ const renderToolsCell = (
2419
+ row: Extract<DisplayRow<T>, { kind: "data" }>,
2420
+ r: number,
2421
+ c: number,
2422
+ position: PinPosition | undefined,
2423
+ pinInfo: ReturnType<typeof rowPinInfo>,
2424
+ sticky: string | undefined | false,
2425
+ ) => {
2426
+ const isOpen = expandedSet.has(row.key);
2427
+ const pinSide = pinnedSideOf(pinnedRowIds, row.key);
2428
+ const name = labelOfRow(row.key);
2429
+ const tool =
2430
+ "grid size-6 shrink-0 place-items-center rounded-xs text-ink-3 hover:bg-surface-2 hover:text-ink focus-visible:outline-2 focus-visible:outline-accent disabled:pointer-events-none disabled:opacity-35 [&_svg]:size-3.5";
2431
+ return (
2432
+ <td
2433
+ key={TOOLS_ID}
2434
+ data-r={r}
2435
+ data-c={c}
2436
+ tabIndex={tabIndexOf(r, c)}
2437
+ role="gridcell"
2438
+ style={{ ...pinStyle(position), ...(pinInfo ? { [pinInfo.side]: pinInfo.offset } : null) }}
2439
+ className={cn(cellBase, sticky, pinShadow(position), "px-1")}
2440
+ >
2441
+ <span className="flex items-center justify-center gap-0.5">
2442
+ {reorderEnabled && !pinInfo ? (
2443
+ <span
2444
+ draggable={reorderAllowed}
2445
+ aria-hidden="true"
2446
+ title={reorderAllowed ? labels.dragRow : labels.reorderBlocked}
2447
+ onDragStart={(event) => onRowDragStart(event, row.key)}
2448
+ onDragEnd={onRowDragEnd}
2449
+ className={cn(
2450
+ tool,
2451
+ reorderAllowed
2452
+ ? "cursor-grab active:cursor-grabbing"
2453
+ : "cursor-not-allowed opacity-35",
2454
+ )}
2455
+ >
2456
+ <GripVertical aria-hidden="true" />
2457
+ </span>
2458
+ ) : reorderEnabled ? (
2459
+ <span className="size-6 shrink-0" aria-hidden="true" />
2460
+ ) : null}
2461
+ {detailEnabled ? (
2462
+ pinInfo ? (
2463
+ <span className="size-6 shrink-0" aria-hidden="true" />
2464
+ ) : (
2465
+ <button
2466
+ type="button"
2467
+ tabIndex={-1}
2468
+ className={tool}
2469
+ aria-expanded={isOpen}
2470
+ aria-label={`${isOpen ? labels.collapseRow : labels.expandRow}: ${name}`}
2471
+ onClick={() => setExpandedRow(row.key)}
2472
+ >
2473
+ <ChevronRight
2474
+ aria-hidden="true"
2475
+ className={cn(
2476
+ "transition-transform motion-reduce:transition-none",
2477
+ isOpen && "rotate-90",
2478
+ )}
2479
+ />
2480
+ </button>
2481
+ )
2482
+ ) : null}
2483
+ {pinningEnabled ? (
2484
+ pinSide ? (
2485
+ <>
2486
+ <button
2487
+ type="button"
2488
+ tabIndex={-1}
2489
+ className={cn(tool, "text-accent-ink")}
2490
+ aria-label={`${labels.unpinRow}: ${name}`}
2491
+ title={labels.unpinRow}
2492
+ onClick={() => pinRow(row.key, pinSide)}
2493
+ >
2494
+ <PinOff aria-hidden="true" />
2495
+ </button>
2496
+ <span className="size-6 shrink-0" aria-hidden="true" />
2497
+ </>
2498
+ ) : (
2499
+ <>
2500
+ <button
2501
+ type="button"
2502
+ tabIndex={-1}
2503
+ className={tool}
2504
+ aria-label={`${labels.pinRowTop}: ${name}`}
2505
+ title={labels.pinRowTop}
2506
+ onClick={() => pinRow(row.key, "top")}
2507
+ >
2508
+ <ArrowUpToLine aria-hidden="true" />
2509
+ </button>
2510
+ <button
2511
+ type="button"
2512
+ tabIndex={-1}
2513
+ className={tool}
2514
+ aria-label={`${labels.pinRowBottom}: ${name}`}
2515
+ title={labels.pinRowBottom}
2516
+ onClick={() => pinRow(row.key, "bottom")}
2517
+ >
2518
+ <ArrowDownToLine aria-hidden="true" />
2519
+ </button>
2520
+ </>
2521
+ )
2522
+ ) : null}
2523
+ </span>
2524
+ </td>
2525
+ );
2526
+ };
2527
+
2528
+ const renderDataRow = (row: Extract<DisplayRow<T>, { kind: "data" }>, r: number) => {
2529
+ const isSelected = selected.has(row.key);
2530
+ const pinInfo = rowPinInfo(r);
2531
+ const isDropTarget = dropRow?.key === row.key;
2532
+ const cells = renderColumns.map((entry, c) => {
2533
+ const position = pinOf(entry);
2534
+ const stuck = !!position || !!pinInfo;
2535
+ const sticky =
2536
+ stuck &&
2537
+ cn(
2538
+ "sticky bg-surface group-hover/row:bg-surface-2 group-aria-selected/row:bg-accent-soft",
2539
+ pinInfo ? (position ? "z-3" : "z-2") : "z-1",
2540
+ );
2541
+ const stuckStyle: CSSProperties = {
2542
+ ...pinStyle(position),
2543
+ ...(pinInfo ? { [pinInfo.side]: pinInfo.offset } : null),
2544
+ };
2545
+ if (entry.kind === "select") {
2546
+ return (
2547
+ <td
2548
+ key={SELECT_ID}
2549
+ data-r={r}
2550
+ data-c={c}
2551
+ tabIndex={tabIndexOf(r, c)}
2552
+ style={stuckStyle}
2553
+ className={cn(cellBase, sticky, pinShadow(position), "px-0 text-center")}
2554
+ >
2555
+ <input
2556
+ type="checkbox"
2557
+ tabIndex={-1}
2558
+ aria-label={`${labels.selectRow} ${dataColumns[0] ? formatCellValue(getCellValue(row.row, dataColumns[0]), dataColumns[0]) : row.key}`}
2559
+ className="size-4 align-middle accent-accent"
2560
+ checked={isSelected}
2561
+ onChange={() => undefined}
2562
+ onClick={(event) => toggleRow(row.key, event.shiftKey)}
2563
+ />
2564
+ </td>
2565
+ );
2566
+ }
2567
+ if (entry.kind === "tools") {
2568
+ return renderToolsCell(row, r, c, position, pinInfo, sticky);
2569
+ }
2570
+ const { column } = entry;
2571
+ const isEditing = editing?.key === row.key && editing.columnId === column.id;
2572
+ const editable = canEdit(column, row.row);
2573
+ const value = getCellValue(row.row, column);
2574
+ const invalid = column.validate ? column.validate(value, row.row) : null;
2575
+ const inSelection = rangeSelection && inRange(cellRange, r, c);
2576
+ const inFill =
2577
+ !!fillPreview &&
2578
+ !inSelection &&
2579
+ r >= fillPreview.r0 &&
2580
+ r <= fillPreview.r1 &&
2581
+ c >= fillPreview.c0 &&
2582
+ c <= fillPreview.c1;
2583
+ const showHandle =
2584
+ canWrite && rangeSelection && !isEditing && !!rangeB && r === rangeB.r1 && c === rangeB.c1;
2585
+ const isTreeCell = !!tree && structured && c === firstDataIndex;
2586
+ const parentAggregate =
2587
+ row.tree?.hasChildren && column.aggregate ? (row.tree.aggregates[column.id] ?? null) : null;
2588
+ const content =
2589
+ parentAggregate !== null && column.aggregate ? (
2590
+ <span className="block truncate font-[650] tabular-nums">
2591
+ <span className="sr-only">{labels.aggregate[column.aggregate]}: </span>
2592
+ {formatAggregate(parentAggregate, column.aggregate, column)}
2593
+ </span>
2594
+ ) : (
2595
+ renderValue(row.row, row.key, column)
2596
+ );
2597
+ const body = isTreeCell ? (
2598
+ <span className="flex min-w-0 items-center gap-1" style={{ paddingLeft: row.depth * 16 }}>
2599
+ {row.tree?.hasChildren ? (
2600
+ <button
2601
+ type="button"
2602
+ tabIndex={-1}
2603
+ aria-label={`${row.tree.expanded ? labels.collapseRow : labels.expandRow}: ${labelOfRow(row.key)}`}
2604
+ aria-expanded={row.tree.expanded}
2605
+ onClick={() => setExpandedRow(row.key)}
2606
+ className="grid size-5 shrink-0 place-items-center rounded-xs text-ink-3 hover:bg-surface-2 hover:text-ink"
2607
+ >
2608
+ <ChevronRight
2609
+ aria-hidden="true"
2610
+ className={cn(
2611
+ "size-3.5 transition-transform motion-reduce:transition-none",
2612
+ row.tree.expanded && "rotate-90",
2613
+ )}
2614
+ />
2615
+ </button>
2616
+ ) : (
2617
+ <span className="size-5 shrink-0" aria-hidden="true" />
2618
+ )}
2619
+ <span className="min-w-0 flex-1">{content}</span>
2620
+ </span>
2621
+ ) : (
2622
+ content
2623
+ );
2624
+ return (
2625
+ <td
2626
+ key={column.id}
2627
+ data-r={r}
2628
+ data-c={c}
2629
+ tabIndex={tabIndexOf(r, c)}
2630
+ role="gridcell"
2631
+ aria-readonly={onCellChange && !editable ? true : undefined}
2632
+ aria-selected={rangeSelection ? inSelection : undefined}
2633
+ aria-invalid={invalid ? true : undefined}
2634
+ title={invalid ? `${labels.invalidCell}: ${invalid}` : undefined}
2635
+ onPointerDown={rangeSelection ? (event) => onCellPointerDown(event, r, c) : undefined}
2636
+ onDoubleClick={() => {
2637
+ if (startEdit(row.key, column)) setActive({ r, c });
2638
+ }}
2639
+ style={{
2640
+ ...stuckStyle,
2641
+ paddingLeft:
2642
+ c === firstDataIndex && row.depth > 0 && !isTreeCell
2643
+ ? 12 + row.depth * 16
2644
+ : undefined,
2645
+ }}
2646
+ className={cn(
2647
+ cellBase,
2648
+ sticky,
2649
+ pinShadow(position),
2650
+ alignClass[columnAlign(column)],
2651
+ (column.type === "number" || column.type === "currency") && "tabular-nums",
2652
+ isEditing && "relative overflow-visible px-1",
2653
+ editable && "cursor-text",
2654
+ invalid && "bg-danger-soft",
2655
+ inSelection && "bg-accent-soft",
2656
+ inFill && "bg-accent-soft/60",
2657
+ showHandle && !sticky && "relative",
2658
+ )}
2659
+ >
2660
+ {showHandle ? (
2661
+ <span
2662
+ data-fill-handle=""
2663
+ role="presentation"
2664
+ title={labels.fillHandle}
2665
+ onPointerDown={onFillHandleDown}
2666
+ className="absolute right-0 bottom-0 z-4 size-2.5 cursor-crosshair touch-none rounded-xs border border-surface bg-accent"
2667
+ />
2668
+ ) : null}
2669
+ {isEditing ? (
2670
+ renderEditor(column, r, c)
2671
+ ) : invalid ? (
2672
+ <span className="flex min-w-0 items-center gap-1.5">
2673
+ <CircleAlert aria-hidden="true" className="size-3.5 shrink-0 text-danger" />
2674
+ <span className="sr-only">
2675
+ {labels.invalidCell}: {invalid.replace(/\.$/, "")}.{" "}
2676
+ </span>
2677
+ <span className="min-w-0 flex-1">{body}</span>
2678
+ </span>
2679
+ ) : (
2680
+ body
2681
+ )}
2682
+ </td>
2683
+ );
2684
+ });
2685
+ return (
2686
+ <tr
2687
+ key={row.key}
2688
+ aria-rowindex={ariaRowOf(r)}
2689
+ aria-selected={selectable ? isSelected : undefined}
2690
+ aria-level={tree && structured ? row.depth + 1 : undefined}
2691
+ aria-expanded={
2692
+ tree && structured && row.tree?.hasChildren
2693
+ ? row.tree.expanded
2694
+ : detailEnabled
2695
+ ? expandedSet.has(row.key)
2696
+ : undefined
2697
+ }
2698
+ data-drop={isDropTarget ? dropRow?.place : undefined}
2699
+ data-pinned={pinInfo?.side}
2700
+ data-divider={dividerRow(r)}
2701
+ style={{ height: rowHeight }}
2702
+ onDragOver={reorderAllowed ? (event) => onRowDragOver(event, row.key) : undefined}
2703
+ onDrop={reorderAllowed ? (event) => onRowDrop(event, row.key) : undefined}
2704
+ className={cn(
2705
+ "group/row text-[13px] text-ink hover:bg-surface-2 aria-selected:bg-accent-soft",
2706
+ "data-[drop=before]:[&>td]:shadow-[inset_0_2px_0_var(--accent)] data-[drop=after]:[&>td]:shadow-[inset_0_-2px_0_var(--accent)]",
2707
+ "data-[divider=top]:[&>td]:border-b-line-strong",
2708
+ )}
2709
+ >
2710
+ {cells.slice(0, fillerIndex)}
2711
+ <td aria-hidden="true" className="border-b-divider" />
2712
+ {cells.slice(fillerIndex)}
2713
+ </tr>
2714
+ );
2715
+ };
2716
+
2717
+ const renderDetailRow = (row: Extract<DisplayRow<T>, { kind: "detail" }>, r: number) => {
2718
+ const labelled = labels.detailOf(labelOfRow(row.rowKey));
2719
+ return (
2720
+ <tr key={row.key} aria-rowindex={ariaRowOf(r)} style={{ height: row.height }}>
2721
+ <td
2722
+ data-r={r}
2723
+ data-c={0}
2724
+ data-c-end={maxC}
2725
+ colSpan={colSpan}
2726
+ role="gridcell"
2727
+ tabIndex={tabbable.r === r ? 0 : -1}
2728
+ aria-label={labelled}
2729
+ className={cn(cellBase, "overflow-hidden bg-surface-2/50 p-0")}
2730
+ >
2731
+ <div
2732
+ data-detail-content=""
2733
+ className="sticky left-0 box-border overflow-auto p-2"
2734
+ style={{ width: containerWidth || "100%", height: row.height - 1 }}
2735
+ >
2736
+ {renderDetail?.(row.row, row.rowKey)}
2737
+ </div>
2738
+ </td>
2739
+ </tr>
2740
+ );
2741
+ };
2742
+
2743
+ const renderPlaceholderRow = (
2744
+ row: Extract<DisplayRow<T>, { kind: "placeholder" }>,
2745
+ r: number,
2746
+ ) => {
2747
+ const cells = renderColumns.map((entry, c) => {
2748
+ const position = pinOf(entry);
2749
+ return (
2750
+ <td
2751
+ key={idOfColumn(entry)}
2752
+ data-r={r}
2753
+ data-c={c}
2754
+ tabIndex={tabIndexOf(r, c)}
2755
+ role="gridcell"
2756
+ style={pinStyle(position)}
2757
+ className={cn(cellBase, position && "sticky z-1 bg-surface", pinShadow(position))}
2758
+ >
2759
+ {entry.kind === "data" ? (
2760
+ <span className="block h-2.5 w-3/4 rounded-pill bg-surface-2 motion-safe:animate-pulse" />
2761
+ ) : null}
2762
+ </td>
2763
+ );
2764
+ });
2765
+ return (
2766
+ <tr key={row.key} aria-rowindex={ariaRowOf(r)} aria-busy="true" style={{ height: rowHeight }}>
2767
+ {cells.slice(0, fillerIndex)}
2768
+ <td aria-hidden="true" className="border-b-divider" />
2769
+ {cells.slice(fillerIndex)}
2770
+ </tr>
2771
+ );
2772
+ };
2773
+
2774
+ const headerCells = renderColumns.map(renderHeader);
2775
+ const footerCells = renderColumns.map((entry, c) => {
2776
+ const position = pinOf(entry);
2777
+ const column = entry.kind === "data" ? entry.column : null;
2778
+ const value = column?.aggregate ? (totals[column.id] ?? null) : null;
2779
+ return (
2780
+ <td
2781
+ key={idOfColumn(entry)}
2782
+ style={pinStyle(position)}
2783
+ className={cn(
2784
+ "sticky bottom-0 overflow-hidden border-t-divider bg-surface px-3 font-[650] text-[13px] text-ink @max-sm/grid:px-2",
2785
+ position ? "z-3" : "z-2",
2786
+ pinShadow(position),
2787
+ column && alignClass[columnAlign(column)],
2788
+ "tabular-nums",
2789
+ )}
2790
+ >
2791
+ {column?.aggregate && value !== null ? (
2792
+ <span className="block truncate">
2793
+ <span className="sr-only">{labels.aggregate[column.aggregate]}: </span>
2794
+ {formatAggregate(value, column.aggregate, column)}
2795
+ </span>
2796
+ ) : c === firstDataIndex ? (
2797
+ <span className="text-[11px] text-ink-3 uppercase tracking-[.07em]">{labels.total}</span>
2798
+ ) : null}
2799
+ </td>
2800
+ );
2801
+ });
2802
+
2803
+ const colSpan = renderColumns.length + 1;
2804
+ const activeFilters = countActiveFilters(filters);
2805
+ const rowCount = bodyLength + groupLevels + 1 + (footer ? 1 : 0);
2806
+ const toolbarRows = sourceActive ? (remote.total ?? 0) : sorted.length;
2807
+
2808
+ useEffect(() => {
2809
+ if (page > pages - 1) setPage(Math.max(0, pages - 1));
2810
+ }, [page, pages]);
2811
+ // biome-ignore lint/correctness/useExhaustiveDependencies: a range refers to rows by position, so any change of the rows drops it.
2812
+ useEffect(() => {
2813
+ setCellRange(null);
2814
+ }, [sort, filters, search, groupBy, page, advanced, data]);
2815
+
2816
+ const renderGroupHeaderRow = (level: number) => {
2817
+ const top = level * GROUP_HEADER_HEIGHT;
2818
+ // Labels of scrolling groups stay readable: they stick next to the pinned-left zone.
2819
+ const labelInset = renderColumns
2820
+ .slice(0, leadingCount + pinned.left.length)
2821
+ .reduce((sum, entry) => sum + widthOf(entry), 0);
2822
+ const out: ReactNode[] = [];
2823
+ let fillerDone = false;
2824
+ const filler = () => {
2825
+ fillerDone = true;
2826
+ out.push(
2827
+ <th
2828
+ key="filler"
2829
+ role="presentation"
2830
+ className="sticky z-2 border-b-divider bg-surface"
2831
+ style={{ top, height: GROUP_HEADER_HEIGHT }}
2832
+ />,
2833
+ );
2834
+ };
2835
+ if (leadingCount > 0) {
2836
+ out.push(
2837
+ <th
2838
+ key="lead"
2839
+ role="presentation"
2840
+ colSpan={leadingCount}
2841
+ className="sticky z-3 border-b-divider bg-surface"
2842
+ style={{ left: 0, top, height: GROUP_HEADER_HEIGHT }}
2843
+ />,
2844
+ );
2845
+ }
2846
+ for (const cell of headerLevels[level] ?? []) {
2847
+ const startIndex = cell.start + leadingCount;
2848
+ if (!fillerDone && startIndex >= fillerIndex) filler();
2849
+ const zone = zoneOf.get(dataColumns[cell.start]?.id ?? "") ?? "center";
2850
+ const first = renderColumns[startIndex];
2851
+ const last = renderColumns[startIndex + cell.span - 1];
2852
+ const style: CSSProperties = { top, height: GROUP_HEADER_HEIGHT };
2853
+ if (zone === "left" && first) style.left = positions[idOfColumn(first)]?.offset;
2854
+ if (zone === "right" && last) style.right = positions[idOfColumn(last)]?.offset;
2855
+ out.push(
2856
+ <th
2857
+ key={`${cell.start}-${cell.label}`}
2858
+ role={cell.label ? "columnheader" : "presentation"}
2859
+ colSpan={cell.span}
2860
+ className={cn(
2861
+ "sticky overflow-clip border-b-divider bg-surface px-3 text-center font-[650] text-[11px] text-ink-3 uppercase tracking-[.07em] @max-sm/grid:px-2",
2862
+ zone === "center" ? "z-2" : "z-3",
2863
+ cell.label && "border-x",
2864
+ )}
2865
+ style={style}
2866
+ >
2867
+ <span
2868
+ className={cn(
2869
+ "block w-fit max-w-full truncate",
2870
+ zone === "center" && "sticky mx-auto @max-sm/grid:mx-0",
2871
+ )}
2872
+ style={zone === "center" ? { left: labelInset + 8 } : undefined}
2873
+ title={cell.label}
2874
+ >
2875
+ {cell.label}
2876
+ </span>
2877
+ </th>,
2878
+ );
2879
+ }
2880
+ if (!fillerDone) filler();
2881
+ return (
2882
+ <tr key={`group-header-${level}`} aria-rowindex={level + 1}>
2883
+ {out}
2884
+ </tr>
2885
+ );
2886
+ };
2887
+
2888
+ const renderBodyRow = (row: DisplayRow<T>, index: number) => {
2889
+ const r = index + 1;
2890
+ switch (row.kind) {
2891
+ case "group":
2892
+ return renderGroupRow(row, r);
2893
+ case "detail":
2894
+ return renderDetailRow(row, r);
2895
+ case "placeholder":
2896
+ return renderPlaceholderRow(row, r);
2897
+ default:
2898
+ return renderDataRow(row, r);
2899
+ }
2900
+ };
2901
+ const bodyRowsIn = (start: number, end: number) =>
2902
+ bodyRows.slice(start, end).map((row, offset) => renderBodyRow(row, start + offset));
2903
+ const spacer = (key: string, px: number) =>
2904
+ px > 0 ? (
2905
+ <tr key={key} aria-hidden="true" style={{ height: px }}>
2906
+ <td colSpan={colSpan} className="p-0" />
2907
+ </tr>
2908
+ ) : null;
2909
+
2910
+ const stats = cellRange
2911
+ ? rangeStats(rangeBounds(cellRange), (r, c) => {
2912
+ const cell = cellAt(r, c);
2913
+ return cell ? getCellValue(cell.row, cell.column) : undefined;
2914
+ })
2915
+ : null;
2916
+ const rangeCellCount = cellRange
2917
+ ? (() => {
2918
+ const b = rangeBounds(cellRange);
2919
+ return (b.r1 - b.r0 + 1) * (b.c1 - b.c0 + 1);
2920
+ })()
2921
+ : 0;
2922
+ const rangeIsCurrency = (() => {
2923
+ if (!cellRange) return false;
2924
+ const b = rangeBounds(cellRange);
2925
+ const kinds = new Set<string>();
2926
+ for (let c = b.c0; c <= b.c1; c += 1) {
2927
+ const entry = renderColumns[c];
2928
+ if (
2929
+ entry?.kind === "data" &&
2930
+ (entry.column.type === "number" || entry.column.type === "currency")
2931
+ ) {
2932
+ kinds.add(entry.column.type);
2933
+ }
2934
+ }
2935
+ return kinds.size === 1 && kinds.has("currency");
2936
+ })();
2937
+ const formatStat = (value: number | null) =>
2938
+ value === null ? "" : rangeIsCurrency ? formatCurrency(value) : formatNumber(value);
2939
+
2940
+ const barCell = formulasOn ? cellAt(activeR, activeC) : null;
2941
+ const [barError, setBarError] = useState<string | undefined>();
2942
+ // biome-ignore lint/correctness/useExhaustiveDependencies: moving to another cell clears the message.
2943
+ useEffect(() => {
2944
+ setBarError(undefined);
2945
+ }, [activeR, activeC]);
2946
+ const barRaw = barCell ? rawValue(barCell.row, barCell.column) : null;
2947
+ const commitBar = (text: string) => {
2948
+ if (!barCell || !canEdit(barCell.column, barCell.row)) return;
2949
+ const result = coerceDraft(barCell.column, barCell.row, text);
2950
+ if ("error" in result) {
2951
+ setBarError(result.error);
2952
+ return;
2953
+ }
2954
+ setBarError(undefined);
2955
+ if (!sameCellValue(result.value, barRaw)) {
2956
+ commitChanges(
2957
+ [
2958
+ {
2959
+ row: barCell.row,
2960
+ rowId: keyOf(barCell.row),
2961
+ column: barCell.column,
2962
+ value: result.value,
2963
+ previous: barRaw,
2964
+ },
2965
+ ],
2966
+ "edit",
2967
+ );
2968
+ }
2969
+ focusCell(activeR, activeC);
2970
+ };
2971
+
2972
+ const emptyFiltered = activeFilters > 0 || search !== "" || advancedCount > 0;
2973
+
2974
+ return (
2975
+ <div
2976
+ className={cn(
2977
+ "@container/grid flex min-w-0 max-w-full flex-col overflow-hidden rounded-card border-card shadow-card surface-card text-ink",
2978
+ className,
2979
+ )}
2980
+ >
2981
+ {hideToolbar ? null : (
2982
+ <GridToolbar
2983
+ labels={labels}
2984
+ search={search}
2985
+ onSearch={(value) => {
2986
+ setSearch(value);
2987
+ resetPage();
2988
+ }}
2989
+ activeFilters={activeFilters}
2990
+ onClearFilters={() => {
2991
+ setFiltersState({});
2992
+ resetPage();
2993
+ }}
2994
+ groupBy={structured ? groupBy : EMPTY_LIST}
2995
+ columns={columns}
2996
+ onUngroup={(id) => setGroup(id, false)}
2997
+ density={density}
2998
+ onDensity={setDensity}
2999
+ hidden={layout.hidden}
3000
+ onHidden={grid.setHidden}
3001
+ onResetColumns={grid.reset}
3002
+ onExport={exportFilename === false ? undefined : exportCsv}
3003
+ onExportXlsx={xlsxName ? exportToXlsx : undefined}
3004
+ exportSelected={selectable && selection.length > 0}
3005
+ rowCount={toolbarRows}
3006
+ selectedCount={selectable ? selection.length : null}
3007
+ actions={toolbarActions}
3008
+ hideColumnPicker={toolPanel}
3009
+ history={
3010
+ historyEnabled
3011
+ ? {
3012
+ canUndo: historySummary.canUndo,
3013
+ canRedo: historySummary.canRedo,
3014
+ onUndo: undo,
3015
+ onRedo: redo,
3016
+ }
3017
+ : undefined
3018
+ }
3019
+ rowTools={
3020
+ structured && onRowsChange && (createRow || selectable)
3021
+ ? {
3022
+ onAdd: createRow ? addRow : undefined,
3023
+ onRemove: selectable ? removeSelectedRows : undefined,
3024
+ removeDisabled: selection.length === 0,
3025
+ }
3026
+ : undefined
3027
+ }
3028
+ toolPanel={
3029
+ toolPanel ? { open: panelOpen, onToggle: () => setPanelOpen(!panelOpen) } : undefined
3030
+ }
3031
+ advancedFilter={
3032
+ showAdvancedFilter && !sourceActive ? (
3033
+ <FilterBuilder
3034
+ columns={columns}
3035
+ value={advanced}
3036
+ onChange={(next) => {
3037
+ setAdvanced(next);
3038
+ resetPage();
3039
+ }}
3040
+ valuesOf={(id) => {
3041
+ const column = byId.get(id);
3042
+ return column
3043
+ ? column.options
3044
+ ? [...column.options]
3045
+ : distinctValues(data, column)
3046
+ : [];
3047
+ }}
3048
+ labels={labelsProp}
3049
+ />
3050
+ ) : undefined
3051
+ }
3052
+ advancedFilterCount={advancedCount}
3053
+ onClearAdvancedFilter={() => setAdvanced(emptyGroup("and", "root"))}
3054
+ />
3055
+ )}
3056
+ {formulasOn ? (
3057
+ <FormulaBar
3058
+ address={
3059
+ barCell && sourceIndex
3060
+ ? cellAddress(
3061
+ sourceIndex.rows.get(barCell.row) ?? 0,
3062
+ sourceIndex.cols.get(barCell.column.id) ?? 0,
3063
+ )
3064
+ : ""
3065
+ }
3066
+ value={barRaw === null || barRaw === undefined ? "" : String(barRaw)}
3067
+ editable={!!barCell && canWrite && canEdit(barCell.column, barCell.row)}
3068
+ error={barError}
3069
+ labels={{ bar: labels.formulaBar, cell: labels.formulaCell, empty: labels.formulaEmpty }}
3070
+ onCommit={commitBar}
3071
+ onDiscard={() => focusCell(activeR, activeC)}
3072
+ />
3073
+ ) : null}
3074
+ {sourceActive && remote.failed && remote.total !== null ? (
3075
+ <div
3076
+ role="alert"
3077
+ className="flex flex-wrap items-center gap-2 border-b-divider bg-danger-soft px-3 py-2 text-[13px] text-danger"
3078
+ >
3079
+ <CircleAlert aria-hidden="true" className="size-4 shrink-0" />
3080
+ <span className="min-w-0 flex-1">{labels.loadError}</span>
3081
+ <button
3082
+ type="button"
3083
+ className="rounded-control border border-danger px-2.5 py-1 font-[600] text-[12.5px] hover:bg-surface"
3084
+ onClick={remote.retry}
3085
+ >
3086
+ {labels.retry}
3087
+ </button>
3088
+ </div>
3089
+ ) : null}
3090
+ <div className="flex min-h-0 min-w-0 flex-col @2xl/grid:flex-row">
3091
+ {toolPanel && panelOpen ? (
3092
+ <GridToolPanel
3093
+ labels={labels}
3094
+ columns={columnsProp}
3095
+ shownColumns={columns}
3096
+ order={layout.order}
3097
+ hidden={layout.hidden}
3098
+ onHidden={grid.setHidden}
3099
+ onMove={grid.move}
3100
+ groupBy={groupBy}
3101
+ onGroup={setGroup}
3102
+ pivot={pivotConfig}
3103
+ onPivot={(next) => {
3104
+ setPivotConfig(next);
3105
+ resetPage();
3106
+ }}
3107
+ pivotValid={pivotValid}
3108
+ canGroup={structured && !tree}
3109
+ canPivot={!sourceActive}
3110
+ maxHeight={height}
3111
+ className="order-first @2xl/grid:order-last"
3112
+ />
3113
+ ) : null}
3114
+ <div
3115
+ ref={scrollerRef}
3116
+ onScroll={syncScroll}
3117
+ className="relative min-h-0 min-w-0 overflow-auto overscroll-contain @2xl/grid:flex-1"
3118
+ style={paged ? undefined : { maxHeight: height }}
3119
+ >
3120
+ <table
3121
+ ref={gridRef}
3122
+ role="grid"
3123
+ aria-label={ariaLabel}
3124
+ aria-describedby={helpId}
3125
+ aria-rowcount={busy ? -1 : rowCount}
3126
+ aria-colcount={renderColumns.length}
3127
+ aria-multiselectable={selectable || rangeSelection || undefined}
3128
+ aria-busy={busy || undefined}
3129
+ onKeyDown={onGridKeyDown}
3130
+ onFocus={onGridFocus}
3131
+ onPaste={onPaste}
3132
+ className={cn(
3133
+ "table-fixed border-separate border-spacing-0",
3134
+ rangeSelection && "select-none",
3135
+ )}
3136
+ style={{ width: totalWidth, minWidth: "100%" }}
3137
+ >
3138
+ <colgroup>
3139
+ {renderColumns.slice(0, fillerIndex).map((entry) => (
3140
+ <col key={idOfColumn(entry)} style={{ width: widthOf(entry) }} />
3141
+ ))}
3142
+ <col />
3143
+ {renderColumns.slice(fillerIndex).map((entry) => (
3144
+ <col key={idOfColumn(entry)} style={{ width: widthOf(entry) }} />
3145
+ ))}
3146
+ </colgroup>
3147
+ <thead>
3148
+ {headerLevels.map((_, level) => renderGroupHeaderRow(level))}
3149
+ <tr aria-rowindex={groupLevels + 1}>
3150
+ {headerCells.slice(0, fillerIndex)}
3151
+ <th
3152
+ role="presentation"
3153
+ className="sticky z-2 border-b-divider bg-surface"
3154
+ style={{ top: headerTop, height: headerHeight }}
3155
+ />
3156
+ {headerCells.slice(fillerIndex)}
3157
+ </tr>
3158
+ </thead>
3159
+ <tbody>
3160
+ {busy ? (
3161
+ Array.from({ length: SKELETON_ROWS }, (_, index) => (
3162
+ // biome-ignore lint/suspicious/noArrayIndexKey: static placeholder rows.
3163
+ <tr key={index} aria-hidden="true" style={{ height: rowHeight }}>
3164
+ {renderColumns.map((entry) => (
3165
+ <td key={idOfColumn(entry)} className={cn(cellBase)}>
3166
+ <span className="block h-2.5 w-3/4 rounded-pill bg-surface-2 motion-safe:animate-pulse" />
3167
+ </td>
3168
+ ))}
3169
+ <td className="border-b-divider" />
3170
+ </tr>
3171
+ ))
3172
+ ) : bodyLength === 0 ? (
3173
+ <tr aria-rowindex={groupLevels + 2}>
3174
+ <td colSpan={colSpan} className="px-4 py-10 text-center text-[13px] text-ink-3">
3175
+ <div className="sticky left-0 inline-flex max-w-[calc(100cqw-2rem)] flex-col items-center gap-2">
3176
+ <span>
3177
+ {sourceActive && remote.failed
3178
+ ? labels.loadError
3179
+ : emptyFiltered
3180
+ ? labels.emptyFiltered
3181
+ : (emptyMessage ?? labels.empty)}
3182
+ </span>
3183
+ {sourceActive && remote.failed ? (
3184
+ <button
3185
+ type="button"
3186
+ className="rounded-control border-divider px-2.5 py-1 font-[600] text-[12.5px] text-ink-2 hover:text-ink"
3187
+ onClick={remote.retry}
3188
+ >
3189
+ {labels.retry}
3190
+ </button>
3191
+ ) : emptyFiltered ? (
3192
+ <button
3193
+ type="button"
3194
+ className="rounded-control border-divider px-2.5 py-1 font-[600] text-[12.5px] text-ink-2 hover:text-ink"
3195
+ onClick={() => {
3196
+ setFiltersState({});
3197
+ setSearch("");
3198
+ setAdvanced(emptyGroup("and", "root"));
3199
+ }}
3200
+ >
3201
+ {labels.clearFilters}
3202
+ </button>
3203
+ ) : null}
3204
+ </div>
3205
+ </td>
3206
+ </tr>
3207
+ ) : (
3208
+ <>
3209
+ {bodyRowsIn(0, topCount)}
3210
+ {spacer("gap-top", gapTop)}
3211
+ {bodyRowsIn(midStart, midEnd)}
3212
+ {spacer("gap-bottom", gapBottom)}
3213
+ {bodyRowsIn(bodyLength - bottomCount, bodyLength)}
3214
+ </>
3215
+ )}
3216
+ </tbody>
3217
+ {footer ? (
3218
+ <tfoot>
3219
+ <tr aria-rowindex={rowCount} style={{ height: rowHeight }}>
3220
+ {footerCells.slice(0, fillerIndex)}
3221
+ <td
3222
+ role="presentation"
3223
+ className="sticky bottom-0 z-2 border-t-divider bg-surface"
3224
+ />
3225
+ {footerCells.slice(fillerIndex)}
3226
+ </tr>
3227
+ </tfoot>
3228
+ ) : null}
3229
+ </table>
3230
+ </div>
3231
+ </div>
3232
+ {rangeSelection ? (
3233
+ <div className="flex flex-wrap items-center gap-x-4 gap-y-1 border-t-divider px-3 py-1.5 text-[12.5px] text-ink-2">
3234
+ {stats && cellRange ? (
3235
+ <>
3236
+ <span className="font-[650] text-ink">{labels.rangeCells(rangeCellCount)}</span>
3237
+ <span>
3238
+ {labels.rangeCount}:{" "}
3239
+ <b className="font-[650] text-ink tabular-nums">{stats.count}</b>
3240
+ </span>
3241
+ {stats.numeric > 0 ? (
3242
+ <>
3243
+ <span>
3244
+ {labels.rangeSum}:{" "}
3245
+ <b className="font-[650] text-ink tabular-nums">{formatStat(stats.sum)}</b>
3246
+ </span>
3247
+ <span>
3248
+ {labels.rangeAvg}:{" "}
3249
+ <b className="font-[650] text-ink tabular-nums">{formatStat(stats.avg)}</b>
3250
+ </span>
3251
+ <span className="@max-sm/grid:hidden">
3252
+ {labels.rangeMin}:{" "}
3253
+ <b className="font-[650] text-ink tabular-nums">{formatStat(stats.min)}</b>
3254
+ </span>
3255
+ <span className="@max-sm/grid:hidden">
3256
+ {labels.rangeMax}:{" "}
3257
+ <b className="font-[650] text-ink tabular-nums">{formatStat(stats.max)}</b>
3258
+ </span>
3259
+ </>
3260
+ ) : null}
3261
+ {charts ? (
3262
+ <button
3263
+ type="button"
3264
+ onClick={openChart}
3265
+ className="ml-auto inline-flex h-7 items-center gap-1.5 rounded-control border-divider bg-surface-control px-2 font-[600] text-[12px] text-ink-2 hover:border-line-strong hover:text-ink [&_svg]:size-3.5"
3266
+ >
3267
+ <BarChart3 aria-hidden="true" />
3268
+ {labels.chart}
3269
+ </button>
3270
+ ) : null}
3271
+ <button
3272
+ type="button"
3273
+ onClick={copyRange}
3274
+ className={cn(
3275
+ "inline-flex h-7 items-center gap-1.5 rounded-control border-divider bg-surface-control px-2 font-[600] text-[12px] text-ink-2 hover:border-line-strong hover:text-ink [&_svg]:size-3.5",
3276
+ !charts && "ml-auto",
3277
+ )}
3278
+ >
3279
+ <Copy aria-hidden="true" />
3280
+ {labels.copyRange}
3281
+ </button>
3282
+ </>
3283
+ ) : (
3284
+ <span className="text-ink-3">{labels.rangeHint}</span>
3285
+ )}
3286
+ </div>
3287
+ ) : null}
3288
+ {charts && chart ? (
3289
+ <Suspense fallback={<div aria-hidden="true" className="h-72 border-t-divider" />}>
3290
+ <ChartPanel
3291
+ data={chart.data}
3292
+ kind={chart.kind}
3293
+ onKind={(kind) => setChart({ ...chart, kind })}
3294
+ onClose={() => setChart(null)}
3295
+ valueFormat={chart.currency ? "currency" : "number"}
3296
+ labels={{
3297
+ title: labels.chartTitle(chart.cells),
3298
+ kinds: labels.chartKinds,
3299
+ kindGroup: labels.chartKindGroup,
3300
+ close: labels.chartClose,
3301
+ noData: labels.chartNoData,
3302
+ other: labels.chartOther,
3303
+ }}
3304
+ />
3305
+ </Suspense>
3306
+ ) : null}
3307
+ <p id={helpId} className="sr-only">
3308
+ {labels.keyboardHelp}
3309
+ </p>
3310
+ <p className="sr-only" role="status" aria-live="polite">
3311
+ {busy ? labels.loading : announcement}
3312
+ </p>
3313
+ {paged && split.middle.length > 0 ? (
3314
+ <Pagination
3315
+ labels={labels}
3316
+ page={currentPage}
3317
+ pages={pages}
3318
+ pageSize={pageSize}
3319
+ total={split.middle.length}
3320
+ options={pageSizeOptions}
3321
+ onPage={setPage}
3322
+ onPageSize={(size) => {
3323
+ setPageSize(size);
3324
+ setPage(0);
3325
+ }}
3326
+ />
3327
+ ) : null}
3328
+ </div>
3329
+ );
3330
+ }
3331
+
3332
+ function Pagination({
3333
+ labels,
3334
+ page,
3335
+ pages,
3336
+ pageSize,
3337
+ total,
3338
+ options,
3339
+ onPage,
3340
+ onPageSize,
3341
+ }: {
3342
+ labels: DataGridLabels;
3343
+ page: number;
3344
+ pages: number;
3345
+ pageSize: number;
3346
+ total: number;
3347
+ options: readonly number[];
3348
+ onPage: (page: number) => void;
3349
+ onPageSize: (size: number) => void;
3350
+ }) {
3351
+ const first = page * pageSize + 1;
3352
+ const last = Math.min(total, (page + 1) * pageSize);
3353
+ const button =
3354
+ "grid size-8 place-items-center rounded-control text-ink-2 hover:bg-surface-2 hover:text-ink disabled:pointer-events-none disabled:opacity-35 [&_svg]:size-4";
3355
+ return (
3356
+ <nav
3357
+ aria-label={labels.rowsPerPage}
3358
+ className="flex flex-wrap items-center justify-end gap-x-3 gap-y-1 border-t-divider px-2 py-1.5 text-[12.5px] text-ink-2"
3359
+ >
3360
+ <label className="flex items-center gap-2">
3361
+ <span>{labels.rowsPerPage}</span>
3362
+ <select
3363
+ value={pageSize}
3364
+ onChange={(event) => onPageSize(Number(event.target.value))}
3365
+ className="h-control-sm rounded-input border-control bg-surface-control px-1.5 text-[12.5px] text-ink"
3366
+ >
3367
+ {[...new Set([...options, pageSize])]
3368
+ .sort((a, b) => a - b)
3369
+ .map((size) => (
3370
+ <option key={size} value={size}>
3371
+ {size}
3372
+ </option>
3373
+ ))}
3374
+ </select>
3375
+ </label>
3376
+ <span aria-live="polite" className="tabular-nums">
3377
+ {labels.pageRange(first, last, total)}
3378
+ </span>
3379
+ <span className="flex items-center">
3380
+ <button
3381
+ type="button"
3382
+ className={button}
3383
+ aria-label={labels.firstPage}
3384
+ disabled={page === 0}
3385
+ onClick={() => onPage(0)}
3386
+ >
3387
+ <ChevronsLeft aria-hidden="true" />
3388
+ </button>
3389
+ <button
3390
+ type="button"
3391
+ className={button}
3392
+ aria-label={labels.previousPage}
3393
+ disabled={page === 0}
3394
+ onClick={() => onPage(page - 1)}
3395
+ >
3396
+ <ChevronLeft aria-hidden="true" />
3397
+ </button>
3398
+ <button
3399
+ type="button"
3400
+ className={button}
3401
+ aria-label={labels.nextPage}
3402
+ disabled={page >= pages - 1}
3403
+ onClick={() => onPage(page + 1)}
3404
+ >
3405
+ <ChevronRight aria-hidden="true" />
3406
+ </button>
3407
+ <button
3408
+ type="button"
3409
+ className={button}
3410
+ aria-label={labels.lastPage}
3411
+ disabled={page >= pages - 1}
3412
+ onClick={() => onPage(pages - 1)}
3413
+ >
3414
+ <ChevronsRight aria-hidden="true" />
3415
+ </button>
3416
+ </span>
3417
+ </nav>
3418
+ );
3419
+ }