@databricks/appkit-ui 0.0.2

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 (359) hide show
  1. package/CLAUDE.md +3 -0
  2. package/DCO +25 -0
  3. package/LICENSE +203 -0
  4. package/NOTICE.md +73 -0
  5. package/README.md +35 -0
  6. package/bin/setup-claude.js +190 -0
  7. package/dist/js/arrow/arrow-client.d.ts +64 -0
  8. package/dist/js/arrow/arrow-client.d.ts.map +1 -0
  9. package/dist/js/arrow/arrow-client.js +181 -0
  10. package/dist/js/arrow/arrow-client.js.map +1 -0
  11. package/dist/js/arrow/index.js +3 -0
  12. package/dist/js/arrow/lazy-arrow.d.ts +23 -0
  13. package/dist/js/arrow/lazy-arrow.d.ts.map +1 -0
  14. package/dist/js/arrow/lazy-arrow.js +86 -0
  15. package/dist/js/arrow/lazy-arrow.js.map +1 -0
  16. package/dist/js/constants.d.ts +10 -0
  17. package/dist/js/constants.d.ts.map +1 -0
  18. package/dist/js/constants.js +30 -0
  19. package/dist/js/constants.js.map +1 -0
  20. package/dist/js/index.d.ts +8 -0
  21. package/dist/js/index.js +8 -0
  22. package/dist/js/sse/connect-sse.d.ts +14 -0
  23. package/dist/js/sse/connect-sse.d.ts.map +1 -0
  24. package/dist/js/sse/connect-sse.js +128 -0
  25. package/dist/js/sse/connect-sse.js.map +1 -0
  26. package/dist/js/sse/types.d.ts +34 -0
  27. package/dist/js/sse/types.d.ts.map +1 -0
  28. package/dist/react/charts/area/index.d.ts +33 -0
  29. package/dist/react/charts/area/index.d.ts.map +1 -0
  30. package/dist/react/charts/area/index.js +29 -0
  31. package/dist/react/charts/area/index.js.map +1 -0
  32. package/dist/react/charts/bar/index.d.ts +43 -0
  33. package/dist/react/charts/bar/index.d.ts.map +1 -0
  34. package/dist/react/charts/bar/index.js +39 -0
  35. package/dist/react/charts/bar/index.js.map +1 -0
  36. package/dist/react/charts/base.d.ts +89 -0
  37. package/dist/react/charts/base.d.ts.map +1 -0
  38. package/dist/react/charts/base.js +123 -0
  39. package/dist/react/charts/base.js.map +1 -0
  40. package/dist/react/charts/chart-error-boundary.js +37 -0
  41. package/dist/react/charts/chart-error-boundary.js.map +1 -0
  42. package/dist/react/charts/constants.d.ts +22 -0
  43. package/dist/react/charts/constants.d.ts.map +1 -0
  44. package/dist/react/charts/constants.js +86 -0
  45. package/dist/react/charts/constants.js.map +1 -0
  46. package/dist/react/charts/create-chart.d.ts +26 -0
  47. package/dist/react/charts/create-chart.d.ts.map +1 -0
  48. package/dist/react/charts/create-chart.js +55 -0
  49. package/dist/react/charts/create-chart.js.map +1 -0
  50. package/dist/react/charts/empty.js +16 -0
  51. package/dist/react/charts/empty.js.map +1 -0
  52. package/dist/react/charts/error.js +16 -0
  53. package/dist/react/charts/error.js.map +1 -0
  54. package/dist/react/charts/heatmap/index.d.ts +42 -0
  55. package/dist/react/charts/heatmap/index.d.ts.map +1 -0
  56. package/dist/react/charts/heatmap/index.js +38 -0
  57. package/dist/react/charts/heatmap/index.js.map +1 -0
  58. package/dist/react/charts/index.js +18 -0
  59. package/dist/react/charts/line/index.d.ts +34 -0
  60. package/dist/react/charts/line/index.d.ts.map +1 -0
  61. package/dist/react/charts/line/index.js +30 -0
  62. package/dist/react/charts/line/index.js.map +1 -0
  63. package/dist/react/charts/loading.js +13 -0
  64. package/dist/react/charts/loading.js.map +1 -0
  65. package/dist/react/charts/normalize.d.ts +37 -0
  66. package/dist/react/charts/normalize.d.ts.map +1 -0
  67. package/dist/react/charts/normalize.js +256 -0
  68. package/dist/react/charts/normalize.js.map +1 -0
  69. package/dist/react/charts/options.d.ts +39 -0
  70. package/dist/react/charts/options.d.ts.map +1 -0
  71. package/dist/react/charts/options.js +212 -0
  72. package/dist/react/charts/options.js.map +1 -0
  73. package/dist/react/charts/pie/index.d.ts +57 -0
  74. package/dist/react/charts/pie/index.d.ts.map +1 -0
  75. package/dist/react/charts/pie/index.js +50 -0
  76. package/dist/react/charts/pie/index.js.map +1 -0
  77. package/dist/react/charts/radar/index.d.ts +32 -0
  78. package/dist/react/charts/radar/index.d.ts.map +1 -0
  79. package/dist/react/charts/radar/index.js +28 -0
  80. package/dist/react/charts/radar/index.js.map +1 -0
  81. package/dist/react/charts/scatter/index.d.ts +32 -0
  82. package/dist/react/charts/scatter/index.d.ts.map +1 -0
  83. package/dist/react/charts/scatter/index.js +28 -0
  84. package/dist/react/charts/scatter/index.js.map +1 -0
  85. package/dist/react/charts/theme.d.ts +23 -0
  86. package/dist/react/charts/theme.d.ts.map +1 -0
  87. package/dist/react/charts/theme.js +96 -0
  88. package/dist/react/charts/theme.js.map +1 -0
  89. package/dist/react/charts/types.d.ts +160 -0
  90. package/dist/react/charts/types.d.ts.map +1 -0
  91. package/dist/react/charts/types.js +17 -0
  92. package/dist/react/charts/types.js.map +1 -0
  93. package/dist/react/charts/utils.d.ts +36 -0
  94. package/dist/react/charts/utils.d.ts.map +1 -0
  95. package/dist/react/charts/utils.js +77 -0
  96. package/dist/react/charts/utils.js.map +1 -0
  97. package/dist/react/charts/wrapper.d.ts +65 -0
  98. package/dist/react/charts/wrapper.d.ts.map +1 -0
  99. package/dist/react/charts/wrapper.js +94 -0
  100. package/dist/react/charts/wrapper.js.map +1 -0
  101. package/dist/react/hooks/index.js +2 -0
  102. package/dist/react/hooks/types.d.ts +101 -0
  103. package/dist/react/hooks/types.d.ts.map +1 -0
  104. package/dist/react/hooks/use-analytics-query.d.ts +33 -0
  105. package/dist/react/hooks/use-analytics-query.d.ts.map +1 -0
  106. package/dist/react/hooks/use-analytics-query.js +146 -0
  107. package/dist/react/hooks/use-analytics-query.js.map +1 -0
  108. package/dist/react/hooks/use-chart-data.d.ts +54 -0
  109. package/dist/react/hooks/use-chart-data.d.ts.map +1 -0
  110. package/dist/react/hooks/use-chart-data.js +80 -0
  111. package/dist/react/hooks/use-chart-data.js.map +1 -0
  112. package/dist/react/hooks/use-mobile.js +21 -0
  113. package/dist/react/hooks/use-mobile.js.map +1 -0
  114. package/dist/react/hooks/use-query-hmr.js +19 -0
  115. package/dist/react/hooks/use-query-hmr.js.map +1 -0
  116. package/dist/react/index.d.ts +75 -0
  117. package/dist/react/index.js +79 -0
  118. package/dist/react/lib/format.js +42 -0
  119. package/dist/react/lib/format.js.map +1 -0
  120. package/dist/react/lib/utils.js +11 -0
  121. package/dist/react/lib/utils.js.map +1 -0
  122. package/dist/react/table/data-table.d.ts +47 -0
  123. package/dist/react/table/data-table.d.ts.map +1 -0
  124. package/dist/react/table/data-table.js +205 -0
  125. package/dist/react/table/data-table.js.map +1 -0
  126. package/dist/react/table/empty.js +16 -0
  127. package/dist/react/table/empty.js.map +1 -0
  128. package/dist/react/table/error.js +16 -0
  129. package/dist/react/table/error.js.map +1 -0
  130. package/dist/react/table/index.js +1 -0
  131. package/dist/react/table/loading.js +50 -0
  132. package/dist/react/table/loading.js.map +1 -0
  133. package/dist/react/table/table-wrapper.js +143 -0
  134. package/dist/react/table/table-wrapper.js.map +1 -0
  135. package/dist/react/table/types.d.ts +55 -0
  136. package/dist/react/table/types.d.ts.map +1 -0
  137. package/dist/react/ui/accordion.d.ts +25 -0
  138. package/dist/react/ui/accordion.d.ts.map +1 -0
  139. package/dist/react/ui/accordion.js +45 -0
  140. package/dist/react/ui/accordion.js.map +1 -0
  141. package/dist/react/ui/alert-dialog.d.ts +49 -0
  142. package/dist/react/ui/alert-dialog.d.ts.map +1 -0
  143. package/dist/react/ui/alert-dialog.js +82 -0
  144. package/dist/react/ui/alert-dialog.js.map +1 -0
  145. package/dist/react/ui/alert.d.ts +25 -0
  146. package/dist/react/ui/alert.d.ts.map +1 -0
  147. package/dist/react/ui/alert.js +38 -0
  148. package/dist/react/ui/alert.js.map +1 -0
  149. package/dist/react/ui/aspect-ratio.d.ts +10 -0
  150. package/dist/react/ui/aspect-ratio.d.ts.map +1 -0
  151. package/dist/react/ui/aspect-ratio.js +16 -0
  152. package/dist/react/ui/aspect-ratio.js.map +1 -0
  153. package/dist/react/ui/avatar.d.ts +20 -0
  154. package/dist/react/ui/avatar.d.ts.map +1 -0
  155. package/dist/react/ui/avatar.js +30 -0
  156. package/dist/react/ui/avatar.js.map +1 -0
  157. package/dist/react/ui/badge.d.ts +20 -0
  158. package/dist/react/ui/badge.d.ts.map +1 -0
  159. package/dist/react/ui/badge.js +26 -0
  160. package/dist/react/ui/badge.js.map +1 -0
  161. package/dist/react/ui/breadcrumb.d.ts +38 -0
  162. package/dist/react/ui/breadcrumb.d.ts.map +1 -0
  163. package/dist/react/ui/breadcrumb.js +71 -0
  164. package/dist/react/ui/breadcrumb.js.map +1 -0
  165. package/dist/react/ui/button-group.d.ts +29 -0
  166. package/dist/react/ui/button-group.d.ts.map +1 -0
  167. package/dist/react/ui/button-group.js +41 -0
  168. package/dist/react/ui/button-group.js.map +1 -0
  169. package/dist/react/ui/button.d.ts +22 -0
  170. package/dist/react/ui/button.d.ts.map +1 -0
  171. package/dist/react/ui/button.js +45 -0
  172. package/dist/react/ui/button.js.map +1 -0
  173. package/dist/react/ui/calendar.d.ts +27 -0
  174. package/dist/react/ui/calendar.d.ts.map +1 -0
  175. package/dist/react/ui/calendar.js +109 -0
  176. package/dist/react/ui/calendar.js.map +1 -0
  177. package/dist/react/ui/card.d.ts +35 -0
  178. package/dist/react/ui/card.d.ts.map +1 -0
  179. package/dist/react/ui/card.js +57 -0
  180. package/dist/react/ui/card.js.map +1 -0
  181. package/dist/react/ui/carousel.d.ts +48 -0
  182. package/dist/react/ui/carousel.d.ts.map +1 -0
  183. package/dist/react/ui/carousel.js +134 -0
  184. package/dist/react/ui/carousel.js.map +1 -0
  185. package/dist/react/ui/chart.d.ts +80 -0
  186. package/dist/react/ui/chart.d.ts.map +1 -0
  187. package/dist/react/ui/chart.js +143 -0
  188. package/dist/react/ui/chart.js.map +1 -0
  189. package/dist/react/ui/checkbox.d.ts +12 -0
  190. package/dist/react/ui/checkbox.d.ts.map +1 -0
  191. package/dist/react/ui/checkbox.js +24 -0
  192. package/dist/react/ui/checkbox.js.map +1 -0
  193. package/dist/react/ui/collapsible.d.ts +16 -0
  194. package/dist/react/ui/collapsible.d.ts.map +1 -0
  195. package/dist/react/ui/collapsible.js +26 -0
  196. package/dist/react/ui/collapsible.js.map +1 -0
  197. package/dist/react/ui/command.d.ts +53 -0
  198. package/dist/react/ui/command.d.ts.map +1 -0
  199. package/dist/react/ui/command.js +87 -0
  200. package/dist/react/ui/command.js.map +1 -0
  201. package/dist/react/ui/context-menu.d.ts +77 -0
  202. package/dist/react/ui/context-menu.d.ts.map +1 -0
  203. package/dist/react/ui/context-menu.js +125 -0
  204. package/dist/react/ui/context-menu.js.map +1 -0
  205. package/dist/react/ui/dialog.d.ts +48 -0
  206. package/dist/react/ui/dialog.d.ts.map +1 -0
  207. package/dist/react/ui/dialog.js +87 -0
  208. package/dist/react/ui/dialog.js.map +1 -0
  209. package/dist/react/ui/drawer.d.ts +45 -0
  210. package/dist/react/ui/drawer.d.ts.map +1 -0
  211. package/dist/react/ui/drawer.js +81 -0
  212. package/dist/react/ui/drawer.js.map +1 -0
  213. package/dist/react/ui/dropdown-menu.d.ts +78 -0
  214. package/dist/react/ui/dropdown-menu.d.ts.map +1 -0
  215. package/dist/react/ui/dropdown-menu.js +124 -0
  216. package/dist/react/ui/dropdown-menu.js.map +1 -0
  217. package/dist/react/ui/empty.d.ts +36 -0
  218. package/dist/react/ui/empty.d.ts.map +1 -0
  219. package/dist/react/ui/empty.js +62 -0
  220. package/dist/react/ui/empty.js.map +1 -0
  221. package/dist/react/ui/field.d.ts +65 -0
  222. package/dist/react/ui/field.d.ts.map +1 -0
  223. package/dist/react/ui/field.js +120 -0
  224. package/dist/react/ui/field.js.map +1 -0
  225. package/dist/react/ui/form.d.ts +46 -0
  226. package/dist/react/ui/form.d.ts.map +1 -0
  227. package/dist/react/ui/form.js +92 -0
  228. package/dist/react/ui/form.js.map +1 -0
  229. package/dist/react/ui/hover-card.d.ts +20 -0
  230. package/dist/react/ui/hover-card.d.ts.map +1 -0
  231. package/dist/react/ui/hover-card.js +35 -0
  232. package/dist/react/ui/hover-card.js.map +1 -0
  233. package/dist/react/ui/index.js +53 -0
  234. package/dist/react/ui/input-group.d.ts +44 -0
  235. package/dist/react/ui/input-group.d.ts.map +1 -0
  236. package/dist/react/ui/input-group.js +82 -0
  237. package/dist/react/ui/input-group.js.map +1 -0
  238. package/dist/react/ui/input-otp.d.ts +29 -0
  239. package/dist/react/ui/input-otp.d.ts.map +1 -0
  240. package/dist/react/ui/input-otp.js +47 -0
  241. package/dist/react/ui/input-otp.js.map +1 -0
  242. package/dist/react/ui/input.d.ts +12 -0
  243. package/dist/react/ui/input.d.ts.map +1 -0
  244. package/dist/react/ui/input.js +16 -0
  245. package/dist/react/ui/input.js.map +1 -0
  246. package/dist/react/ui/item.d.ts +63 -0
  247. package/dist/react/ui/item.d.ts.map +1 -0
  248. package/dist/react/ui/item.js +118 -0
  249. package/dist/react/ui/item.js.map +1 -0
  250. package/dist/react/ui/kbd.d.ts +14 -0
  251. package/dist/react/ui/kbd.d.ts.map +1 -0
  252. package/dist/react/ui/kbd.js +22 -0
  253. package/dist/react/ui/kbd.js.map +1 -0
  254. package/dist/react/ui/label.d.ts +12 -0
  255. package/dist/react/ui/label.d.ts.map +1 -0
  256. package/dist/react/ui/label.js +18 -0
  257. package/dist/react/ui/label.js.map +1 -0
  258. package/dist/react/ui/menubar.d.ts +85 -0
  259. package/dist/react/ui/menubar.d.ts.map +1 -0
  260. package/dist/react/ui/menubar.js +134 -0
  261. package/dist/react/ui/menubar.js.map +1 -0
  262. package/dist/react/ui/navigation-menu.d.ts +47 -0
  263. package/dist/react/ui/navigation-menu.d.ts.map +1 -0
  264. package/dist/react/ui/navigation-menu.js +82 -0
  265. package/dist/react/ui/navigation-menu.js.map +1 -0
  266. package/dist/react/ui/pagination.d.ts +40 -0
  267. package/dist/react/ui/pagination.d.ts.map +1 -0
  268. package/dist/react/ui/pagination.js +80 -0
  269. package/dist/react/ui/pagination.js.map +1 -0
  270. package/dist/react/ui/popover.d.ts +23 -0
  271. package/dist/react/ui/popover.d.ts.map +1 -0
  272. package/dist/react/ui/popover.js +38 -0
  273. package/dist/react/ui/popover.js.map +1 -0
  274. package/dist/react/ui/progress.d.ts +13 -0
  275. package/dist/react/ui/progress.d.ts.map +1 -0
  276. package/dist/react/ui/progress.js +21 -0
  277. package/dist/react/ui/progress.js.map +1 -0
  278. package/dist/react/ui/radio-group.d.ts +16 -0
  279. package/dist/react/ui/radio-group.d.ts.map +1 -0
  280. package/dist/react/ui/radio-group.js +31 -0
  281. package/dist/react/ui/radio-group.js.map +1 -0
  282. package/dist/react/ui/resizable.d.ts +22 -0
  283. package/dist/react/ui/resizable.d.ts.map +1 -0
  284. package/dist/react/ui/resizable.js +34 -0
  285. package/dist/react/ui/resizable.js.map +1 -0
  286. package/dist/react/ui/scroll-area.d.ts +18 -0
  287. package/dist/react/ui/scroll-area.d.ts.map +1 -0
  288. package/dist/react/ui/scroll-area.js +39 -0
  289. package/dist/react/ui/scroll-area.js.map +1 -0
  290. package/dist/react/ui/select.d.ts +53 -0
  291. package/dist/react/ui/select.d.ts.map +1 -0
  292. package/dist/react/ui/select.js +98 -0
  293. package/dist/react/ui/select.js.map +1 -0
  294. package/dist/react/ui/separator.d.ts +14 -0
  295. package/dist/react/ui/separator.d.ts.map +1 -0
  296. package/dist/react/ui/separator.js +20 -0
  297. package/dist/react/ui/separator.js.map +1 -0
  298. package/dist/react/ui/sheet.d.ts +41 -0
  299. package/dist/react/ui/sheet.d.ts.map +1 -0
  300. package/dist/react/ui/sheet.js +83 -0
  301. package/dist/react/ui/sheet.js.map +1 -0
  302. package/dist/react/ui/sidebar.d.ts +167 -0
  303. package/dist/react/ui/sidebar.d.ts.map +1 -0
  304. package/dist/react/ui/sidebar.js +379 -0
  305. package/dist/react/ui/sidebar.js.map +1 -0
  306. package/dist/react/ui/skeleton.d.ts +10 -0
  307. package/dist/react/ui/skeleton.d.ts.map +1 -0
  308. package/dist/react/ui/skeleton.js +15 -0
  309. package/dist/react/ui/skeleton.js.map +1 -0
  310. package/dist/react/ui/slider.d.ts +16 -0
  311. package/dist/react/ui/slider.d.ts.map +1 -0
  312. package/dist/react/ui/slider.js +40 -0
  313. package/dist/react/ui/slider.js.map +1 -0
  314. package/dist/react/ui/sonner.d.ts +10 -0
  315. package/dist/react/ui/sonner.d.ts.map +1 -0
  316. package/dist/react/ui/sonner.js +31 -0
  317. package/dist/react/ui/sonner.js.map +1 -0
  318. package/dist/react/ui/spinner.d.ts +10 -0
  319. package/dist/react/ui/spinner.d.ts.map +1 -0
  320. package/dist/react/ui/spinner.js +17 -0
  321. package/dist/react/ui/spinner.js.map +1 -0
  322. package/dist/react/ui/switch.d.ts +12 -0
  323. package/dist/react/ui/switch.d.ts.map +1 -0
  324. package/dist/react/ui/switch.js +22 -0
  325. package/dist/react/ui/switch.js.map +1 -0
  326. package/dist/react/ui/table.d.ts +39 -0
  327. package/dist/react/ui/table.d.ts.map +1 -0
  328. package/dist/react/ui/table.js +68 -0
  329. package/dist/react/ui/table.js.map +1 -0
  330. package/dist/react/ui/tabs.d.ts +24 -0
  331. package/dist/react/ui/tabs.d.ts.map +1 -0
  332. package/dist/react/ui/tabs.js +39 -0
  333. package/dist/react/ui/tabs.js.map +1 -0
  334. package/dist/react/ui/textarea.d.ts +11 -0
  335. package/dist/react/ui/textarea.d.ts.map +1 -0
  336. package/dist/react/ui/textarea.js +15 -0
  337. package/dist/react/ui/textarea.js.map +1 -0
  338. package/dist/react/ui/toggle-group.d.ts +27 -0
  339. package/dist/react/ui/toggle-group.d.ts.map +1 -0
  340. package/dist/react/ui/toggle-group.js +50 -0
  341. package/dist/react/ui/toggle-group.js.map +1 -0
  342. package/dist/react/ui/toggle.d.ts +20 -0
  343. package/dist/react/ui/toggle.d.ts.map +1 -0
  344. package/dist/react/ui/toggle.js +38 -0
  345. package/dist/react/ui/toggle.js.map +1 -0
  346. package/dist/react/ui/tooltip.d.ts +24 -0
  347. package/dist/react/ui/tooltip.d.ts.map +1 -0
  348. package/dist/react/ui/tooltip.js +39 -0
  349. package/dist/react/ui/tooltip.js.map +1 -0
  350. package/dist/shared/src/sql/helpers.d.ts +160 -0
  351. package/dist/shared/src/sql/helpers.d.ts.map +1 -0
  352. package/dist/shared/src/sql/helpers.js +103 -0
  353. package/dist/shared/src/sql/helpers.js.map +1 -0
  354. package/dist/shared/src/sql/types.d.ts +34 -0
  355. package/dist/shared/src/sql/types.d.ts.map +1 -0
  356. package/dist/styles.css +425 -0
  357. package/llms.txt +193 -0
  358. package/package.json +98 -0
  359. package/scripts/postinstall.js +6 -0
@@ -0,0 +1,42 @@
1
+ //#region src/react/lib/format.ts
2
+ /**
3
+ * Formats numeric values based on field name context
4
+ * @param value - The numeric value to format
5
+ * @param fieldName - The field name to determine formatting
6
+ * @returns Formatted string representation
7
+ * @example
8
+ * formatChartValue(1234.56, "cost") // "$1,234.56"
9
+ * formatChartValue(5000, "users") // "5.0k"
10
+ * formatChartValue(42.7, "percentage") // "42.7"
11
+ */
12
+ function formatChartValue(value, fieldName) {
13
+ if (fieldName.toLowerCase().includes("cost") || fieldName.toLowerCase().includes("price") || fieldName.toLowerCase().includes("spend") || fieldName.toLowerCase().includes("revenue") || fieldName.toLowerCase().includes("usd")) return new Intl.NumberFormat("en-US", {
14
+ style: "currency",
15
+ currency: "USD",
16
+ minimumFractionDigits: 2,
17
+ maximumFractionDigits: 2
18
+ }).format(value);
19
+ if (value >= 1e3) return new Intl.NumberFormat("en-US", {
20
+ notation: "compact",
21
+ maximumFractionDigits: 1
22
+ }).format(value);
23
+ return value.toLocaleString("en-US", { maximumFractionDigits: 2 });
24
+ }
25
+ /**
26
+ * Converts field names to human-readable labels
27
+ * @param field - Field name in camelCase or snake_case
28
+ * @returns Formatted label with proper capitalization
29
+ * @example
30
+ * formatFieldLabel("totalCost") // "Total Cost"
31
+ * formatFieldLabel("user_name") // "User Name"
32
+ * formatFieldLabel("revenue") // "Revenue"
33
+ */
34
+ function formatFieldLabel(field) {
35
+ return field.replace(/[^a-zA-Z0-9_-]/g, "").replace(/([A-Z])/g, " $1").replace(/_/g, " ").replace(/\b\w/g, (l) => l.toUpperCase()).trim();
36
+ }
37
+ /** Regex for validating field names */
38
+ const SAFE_KEY_REGEX = /^[a-zA-Z_][a-zA-Z0-9_]*$/;
39
+
40
+ //#endregion
41
+ export { SAFE_KEY_REGEX, formatChartValue, formatFieldLabel };
42
+ //# sourceMappingURL=format.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"format.js","names":[],"sources":["../../../src/react/lib/format.ts"],"sourcesContent":["/**\n * Formats numeric values based on field name context\n * @param value - The numeric value to format\n * @param fieldName - The field name to determine formatting\n * @returns Formatted string representation\n * @example\n * formatChartValue(1234.56, \"cost\") // \"$1,234.56\"\n * formatChartValue(5000, \"users\") // \"5.0k\"\n * formatChartValue(42.7, \"percentage\") // \"42.7\"\n */\nexport function formatChartValue(value: number, fieldName: string): string {\n if (\n fieldName.toLowerCase().includes(\"cost\") ||\n fieldName.toLowerCase().includes(\"price\") ||\n fieldName.toLowerCase().includes(\"spend\") ||\n fieldName.toLowerCase().includes(\"revenue\") ||\n fieldName.toLowerCase().includes(\"usd\")\n ) {\n return new Intl.NumberFormat(\"en-US\", {\n style: \"currency\",\n currency: \"USD\",\n minimumFractionDigits: 2,\n maximumFractionDigits: 2,\n }).format(value);\n }\n\n if (value >= 1000) {\n return new Intl.NumberFormat(\"en-US\", {\n notation: \"compact\",\n maximumFractionDigits: 1,\n }).format(value);\n }\n\n return value.toLocaleString(\"en-US\", {\n maximumFractionDigits: 2,\n });\n}\n\n/**\n * Converts field names to human-readable labels\n * @param field - Field name in camelCase or snake_case\n * @returns Formatted label with proper capitalization\n * @example\n * formatFieldLabel(\"totalCost\") // \"Total Cost\"\n * formatFieldLabel(\"user_name\") // \"User Name\"\n * formatFieldLabel(\"revenue\") // \"Revenue\"\n */\nexport function formatFieldLabel(field: string): string {\n const safe = field.replace(/[^a-zA-Z0-9_-]/g, \"\");\n return safe\n .replace(/([A-Z])/g, \" $1\")\n .replace(/_/g, \" \")\n .replace(/\\b\\w/g, (l) => l.toUpperCase())\n .trim();\n}\n\n/** Regex for validating field names */\nexport const SAFE_KEY_REGEX = /^[a-zA-Z_][a-zA-Z0-9_]*$/;\n"],"mappings":";;;;;;;;;;;AAUA,SAAgB,iBAAiB,OAAe,WAA2B;AACzE,KACE,UAAU,aAAa,CAAC,SAAS,OAAO,IACxC,UAAU,aAAa,CAAC,SAAS,QAAQ,IACzC,UAAU,aAAa,CAAC,SAAS,QAAQ,IACzC,UAAU,aAAa,CAAC,SAAS,UAAU,IAC3C,UAAU,aAAa,CAAC,SAAS,MAAM,CAEvC,QAAO,IAAI,KAAK,aAAa,SAAS;EACpC,OAAO;EACP,UAAU;EACV,uBAAuB;EACvB,uBAAuB;EACxB,CAAC,CAAC,OAAO,MAAM;AAGlB,KAAI,SAAS,IACX,QAAO,IAAI,KAAK,aAAa,SAAS;EACpC,UAAU;EACV,uBAAuB;EACxB,CAAC,CAAC,OAAO,MAAM;AAGlB,QAAO,MAAM,eAAe,SAAS,EACnC,uBAAuB,GACxB,CAAC;;;;;;;;;;;AAYJ,SAAgB,iBAAiB,OAAuB;AAEtD,QADa,MAAM,QAAQ,mBAAmB,GAAG,CAE9C,QAAQ,YAAY,MAAM,CAC1B,QAAQ,MAAM,IAAI,CAClB,QAAQ,UAAU,MAAM,EAAE,aAAa,CAAC,CACxC,MAAM;;;AAIX,MAAa,iBAAiB"}
@@ -0,0 +1,11 @@
1
+ import { clsx } from "clsx";
2
+ import { twMerge } from "tailwind-merge";
3
+
4
+ //#region src/react/lib/utils.ts
5
+ function cn(...inputs) {
6
+ return twMerge(clsx(inputs));
7
+ }
8
+
9
+ //#endregion
10
+ export { cn };
11
+ //# sourceMappingURL=utils.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"utils.js","names":[],"sources":["../../../src/react/lib/utils.ts"],"sourcesContent":["import { type ClassValue, clsx } from \"clsx\";\nimport { twMerge } from \"tailwind-merge\";\n\nexport function cn(...inputs: ClassValue[]) {\n return twMerge(clsx(inputs));\n}\n"],"mappings":";;;;AAGA,SAAgB,GAAG,GAAG,QAAsB;AAC1C,QAAO,QAAQ,KAAK,OAAO,CAAC"}
@@ -0,0 +1,47 @@
1
+ import { DataTableProps } from "./types.js";
2
+ import * as react_jsx_runtime0 from "react/jsx-runtime";
3
+
4
+ //#region src/react/table/data-table.d.ts
5
+
6
+ /**
7
+ * Production-ready data table with automatic data fetching and state management
8
+ * Features:
9
+ * - Automatic column generation from data structure
10
+ * - Integrated with useAnalyticsQuery for data fetching
11
+ * - Built-in loading, error, and empty states
12
+ * - Dynamic filtering, sorting and pagination
13
+ * - Column visibility controls
14
+ * - Responsive design
15
+ * @param props - Props for the DataTable component
16
+ * @param props.queryKey - The query key to fetch the data
17
+ * @param props.parameters - The parameters to pass to the query
18
+ * @param props.filterColumn - The column to filter by
19
+ * @param props.filterPlaceholder - The placeholder for the filter input
20
+ * @param props.children - Optional children for full control mode
21
+ * @returns - The rendered data table component
22
+ *
23
+ * @example
24
+ * // Opinionated mode
25
+ * <DataTable
26
+ * queryKey="users-list"
27
+ * parameters={{ status: "active" }}
28
+ * filterColumn="email"
29
+ * filterPlaceholder="Filter by email..."
30
+ * />
31
+ * @example
32
+ * // full control mode
33
+ * <DataTable queryKey="users-list" parameters={{ status: "active" }}>
34
+ * {(table) => (
35
+ * <div>
36
+ * <h2>Custom Table UI</h2>
37
+ * {table.getRowModel().rows.map(row => (
38
+ * <div key={row.id}>{row.original.name}</div>
39
+ * ))}
40
+ * </div>
41
+ * )}
42
+ * </DataTable>
43
+ */
44
+ declare function DataTable(props: DataTableProps): react_jsx_runtime0.JSX.Element;
45
+ //#endregion
46
+ export { DataTable };
47
+ //# sourceMappingURL=data-table.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"data-table.d.ts","names":[],"sources":["../../../src/react/table/data-table.tsx"],"sourcesContent":[],"mappings":";;;;;;;;AAmEA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAAgB,SAAA,QAAiB,iBAAc,kBAAA,CAAA,GAAA,CAAA"}
@@ -0,0 +1,205 @@
1
+ import { formatFieldLabel } from "../lib/format.js";
2
+ import { Button } from "../ui/button.js";
3
+ import { DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuTrigger } from "../ui/dropdown-menu.js";
4
+ import { Input } from "../ui/input.js";
5
+ import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "../ui/select.js";
6
+ import { Table as Table$1, TableBody, TableCell, TableHead, TableHeader, TableRow } from "../ui/table.js";
7
+ import { TableWrapper } from "./table-wrapper.js";
8
+ import { jsx, jsxs } from "react/jsx-runtime";
9
+ import { flexRender } from "@tanstack/react-table";
10
+ import { ChevronDown } from "lucide-react";
11
+
12
+ //#region src/react/table/data-table.tsx
13
+ /**
14
+ * Production-ready data table with automatic data fetching and state management
15
+ * Features:
16
+ * - Automatic column generation from data structure
17
+ * - Integrated with useAnalyticsQuery for data fetching
18
+ * - Built-in loading, error, and empty states
19
+ * - Dynamic filtering, sorting and pagination
20
+ * - Column visibility controls
21
+ * - Responsive design
22
+ * @param props - Props for the DataTable component
23
+ * @param props.queryKey - The query key to fetch the data
24
+ * @param props.parameters - The parameters to pass to the query
25
+ * @param props.filterColumn - The column to filter by
26
+ * @param props.filterPlaceholder - The placeholder for the filter input
27
+ * @param props.children - Optional children for full control mode
28
+ * @returns - The rendered data table component
29
+ *
30
+ * @example
31
+ * // Opinionated mode
32
+ * <DataTable
33
+ * queryKey="users-list"
34
+ * parameters={{ status: "active" }}
35
+ * filterColumn="email"
36
+ * filterPlaceholder="Filter by email..."
37
+ * />
38
+ * @example
39
+ * // full control mode
40
+ * <DataTable queryKey="users-list" parameters={{ status: "active" }}>
41
+ * {(table) => (
42
+ * <div>
43
+ * <h2>Custom Table UI</h2>
44
+ * {table.getRowModel().rows.map(row => (
45
+ * <div key={row.id}>{row.original.name}</div>
46
+ * ))}
47
+ * </div>
48
+ * )}
49
+ * </DataTable>
50
+ */
51
+ function DataTable(props) {
52
+ const { parameters, queryKey, filterColumn, filterPlaceholder, transform, labels, ariaLabel, testId, className, enableRowSelection, onRowSelectionChange, children, pageSize = 10, pageSizeOptions = [
53
+ 10,
54
+ 25,
55
+ 50,
56
+ 100
57
+ ] } = props;
58
+ const finalLabels = {
59
+ columnsButton: "Columns",
60
+ noResults: "No results found.",
61
+ rowsFound: `\${count} row(s) found`,
62
+ previousButton: "Previous",
63
+ nextButton: "Next",
64
+ rowsPerPage: "Rows per page",
65
+ showing: `Showing \${from} to \${to} of \${total}`,
66
+ ...labels
67
+ };
68
+ return /* @__PURE__ */ jsx(TableWrapper, {
69
+ queryKey,
70
+ parameters,
71
+ ariaLabel,
72
+ testId,
73
+ className,
74
+ transformer: transform,
75
+ enableRowSelection,
76
+ onRowSelectionChange,
77
+ pageSize,
78
+ children: (table) => {
79
+ if (children) return children(table);
80
+ const data = table.options.data;
81
+ const defaultFilterColumn = filterColumn || (data && data.length > 0 ? Object.keys(data[0]).find((key) => typeof data[0][key] === "string") : null);
82
+ const totalRows = table.getFilteredRowModel().rows.length;
83
+ const currentPage = table.getState().pagination.pageIndex + 1;
84
+ const currentPageSize = table.getState().pagination.pageSize;
85
+ const fromRow = totalRows === 0 ? 0 : (currentPage - 1) * currentPageSize + 1;
86
+ const toRow = Math.min(currentPage * currentPageSize, totalRows);
87
+ return /* @__PURE__ */ jsxs("div", {
88
+ className: "w-full",
89
+ children: [
90
+ /* @__PURE__ */ jsxs("div", {
91
+ className: "flex items-center py-4 gap-2",
92
+ children: [defaultFilterColumn && /* @__PURE__ */ jsx(Input, {
93
+ placeholder: filterPlaceholder || `Filter by ${formatFieldLabel(defaultFilterColumn)}...`,
94
+ value: table.getColumn(defaultFilterColumn)?.getFilterValue() ?? "",
95
+ onChange: (event) => table.getColumn(defaultFilterColumn)?.setFilterValue(event.target.value),
96
+ className: "max-w-sm"
97
+ }), /* @__PURE__ */ jsxs(DropdownMenu, { children: [/* @__PURE__ */ jsx(DropdownMenuTrigger, {
98
+ asChild: true,
99
+ children: /* @__PURE__ */ jsxs(Button, {
100
+ variant: "outline",
101
+ className: "ml-auto",
102
+ children: [
103
+ finalLabels.columnsButton,
104
+ " ",
105
+ /* @__PURE__ */ jsx(ChevronDown, { className: "ml-2 h-4 w-4" })
106
+ ]
107
+ })
108
+ }), /* @__PURE__ */ jsx(DropdownMenuContent, {
109
+ align: "end",
110
+ children: table.getAllColumns().filter((column) => column.getCanHide()).map((column) => {
111
+ return /* @__PURE__ */ jsx(DropdownMenuCheckboxItem, {
112
+ className: "capitalize",
113
+ checked: column.getIsVisible(),
114
+ onCheckedChange: (value) => column.toggleVisibility(!!value),
115
+ children: formatFieldLabel(column.id)
116
+ }, column.id);
117
+ })
118
+ })] })]
119
+ }),
120
+ /* @__PURE__ */ jsx("div", {
121
+ className: "overflow-hidden rounded-md border border-border",
122
+ children: /* @__PURE__ */ jsx("div", {
123
+ className: "max-h-[600px] overflow-y-auto",
124
+ children: /* @__PURE__ */ jsxs(Table$1, { children: [/* @__PURE__ */ jsx(TableHeader, {
125
+ className: "sticky top-0 bg-background z-10",
126
+ children: table.getHeaderGroups().map((headerGroup) => /* @__PURE__ */ jsx(TableRow, { children: headerGroup.headers.map((header) => {
127
+ const isSelectColumn = header.column.id === "select";
128
+ return /* @__PURE__ */ jsx(TableHead, {
129
+ style: {
130
+ width: header.getSize(),
131
+ position: "relative"
132
+ },
133
+ className: isSelectColumn ? "w-[40px] p-0" : void 0,
134
+ children: header.isPlaceholder ? null : flexRender(header.column.columnDef.header, header.getContext())
135
+ }, header.id);
136
+ }) }, headerGroup.id))
137
+ }), /* @__PURE__ */ jsx(TableBody, { children: table.getRowModel().rows?.length ? table.getRowModel().rows.map((row) => /* @__PURE__ */ jsx(TableRow, {
138
+ "data-state": row.getIsSelected() && "selected",
139
+ children: row.getVisibleCells().map((cell) => {
140
+ const isSelectColumn = cell.column.id === "select";
141
+ return /* @__PURE__ */ jsx(TableCell, {
142
+ style: { width: cell.column.getSize() },
143
+ className: isSelectColumn ? "w-[40px] p-0" : void 0,
144
+ children: flexRender(cell.column.columnDef.cell, cell.getContext())
145
+ }, cell.id);
146
+ })
147
+ }, row.id)) : /* @__PURE__ */ jsx(TableRow, { children: /* @__PURE__ */ jsx(TableCell, {
148
+ colSpan: table.getAllColumns().length,
149
+ className: "h-24 text-center",
150
+ children: finalLabels.noResults
151
+ }) }) })] })
152
+ })
153
+ }),
154
+ /* @__PURE__ */ jsxs("div", {
155
+ className: "flex items-center justify-between py-4",
156
+ children: [/* @__PURE__ */ jsxs("div", {
157
+ className: "flex items-center gap-6",
158
+ children: [/* @__PURE__ */ jsx("div", {
159
+ className: "text-foreground text-sm",
160
+ children: totalRows > 0 ? finalLabels.showing.replace(`\${from}`, fromRow.toString()).replace(`\${to}`, toRow.toString()).replace(`\${total}`, totalRows.toString()) : finalLabels.noResults
161
+ }), /* @__PURE__ */ jsxs("div", {
162
+ className: "flex items-center gap-2",
163
+ children: [/* @__PURE__ */ jsx("span", {
164
+ className: "text-sm text-foreground",
165
+ children: finalLabels.rowsPerPage
166
+ }), /* @__PURE__ */ jsxs(Select, {
167
+ value: currentPageSize.toString(),
168
+ onValueChange: (value) => {
169
+ table.setPageSize(Number(value));
170
+ },
171
+ children: [/* @__PURE__ */ jsx(SelectTrigger, {
172
+ className: "w-[70px]",
173
+ children: /* @__PURE__ */ jsx(SelectValue, {})
174
+ }), /* @__PURE__ */ jsx(SelectContent, { children: pageSizeOptions.map((size) => /* @__PURE__ */ jsx(SelectItem, {
175
+ value: size.toString(),
176
+ children: size
177
+ }, size)) })]
178
+ })]
179
+ })]
180
+ }), /* @__PURE__ */ jsxs("div", {
181
+ className: "space-x-2",
182
+ children: [/* @__PURE__ */ jsx(Button, {
183
+ variant: "outline",
184
+ size: "sm",
185
+ onClick: () => table.previousPage(),
186
+ disabled: !table.getCanPreviousPage(),
187
+ children: finalLabels.previousButton
188
+ }), /* @__PURE__ */ jsx(Button, {
189
+ variant: "outline",
190
+ size: "sm",
191
+ onClick: () => table.nextPage(),
192
+ disabled: !table.getCanNextPage(),
193
+ children: finalLabels.nextButton
194
+ })]
195
+ })]
196
+ })
197
+ ]
198
+ });
199
+ }
200
+ });
201
+ }
202
+
203
+ //#endregion
204
+ export { DataTable };
205
+ //# sourceMappingURL=data-table.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"data-table.js","names":["Table"],"sources":["../../../src/react/table/data-table.tsx"],"sourcesContent":["import { flexRender } from \"@tanstack/react-table\";\nimport { ChevronDown } from \"lucide-react\";\nimport { formatFieldLabel } from \"../lib/format\";\nimport { Button } from \"../ui/button\";\nimport {\n DropdownMenu,\n DropdownMenuCheckboxItem,\n DropdownMenuContent,\n DropdownMenuTrigger,\n} from \"../ui/dropdown-menu\";\nimport { Input } from \"../ui/input\";\nimport {\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n} from \"../ui/select\";\nimport {\n Table,\n TableBody,\n TableCell,\n TableHead,\n TableHeader,\n TableRow,\n} from \"../ui/table\";\nimport { TableWrapper } from \"./table-wrapper\";\nimport type { DataTableLabels, DataTableProps } from \"./types\";\n\n/**\n * Production-ready data table with automatic data fetching and state management\n * Features:\n * - Automatic column generation from data structure\n * - Integrated with useAnalyticsQuery for data fetching\n * - Built-in loading, error, and empty states\n * - Dynamic filtering, sorting and pagination\n * - Column visibility controls\n * - Responsive design\n * @param props - Props for the DataTable component\n * @param props.queryKey - The query key to fetch the data\n * @param props.parameters - The parameters to pass to the query\n * @param props.filterColumn - The column to filter by\n * @param props.filterPlaceholder - The placeholder for the filter input\n * @param props.children - Optional children for full control mode\n * @returns - The rendered data table component\n *\n * @example\n * // Opinionated mode\n * <DataTable\n * queryKey=\"users-list\"\n * parameters={{ status: \"active\" }}\n * filterColumn=\"email\"\n * filterPlaceholder=\"Filter by email...\"\n * />\n * @example\n * // full control mode\n * <DataTable queryKey=\"users-list\" parameters={{ status: \"active\" }}>\n * {(table) => (\n * <div>\n * <h2>Custom Table UI</h2>\n * {table.getRowModel().rows.map(row => (\n * <div key={row.id}>{row.original.name}</div>\n * ))}\n * </div>\n * )}\n * </DataTable>\n */\nexport function DataTable(props: DataTableProps) {\n const {\n parameters,\n queryKey,\n filterColumn,\n filterPlaceholder,\n transform,\n labels,\n ariaLabel,\n testId,\n className,\n enableRowSelection,\n onRowSelectionChange,\n children,\n pageSize = 10,\n pageSizeOptions = [10, 25, 50, 100],\n } = props;\n\n const defaultLabels: Required<DataTableLabels> = {\n columnsButton: \"Columns\",\n noResults: \"No results found.\",\n rowsFound: `\\${count} row(s) found`,\n previousButton: \"Previous\",\n nextButton: \"Next\",\n rowsPerPage: \"Rows per page\",\n showing: `Showing \\${from} to \\${to} of \\${total}`,\n };\n\n const finalLabels = { ...defaultLabels, ...labels };\n\n return (\n <TableWrapper\n queryKey={queryKey}\n parameters={parameters}\n ariaLabel={ariaLabel}\n testId={testId}\n className={className}\n transformer={transform}\n enableRowSelection={enableRowSelection}\n onRowSelectionChange={onRowSelectionChange}\n pageSize={pageSize}\n >\n {(table) => {\n if (children) {\n return children(table);\n }\n\n const data = table.options.data;\n\n const defaultFilterColumn =\n filterColumn ||\n (data && data.length > 0\n ? Object.keys(data[0] as Record<string, any>).find(\n (key) =>\n typeof (data[0] as Record<string, any>)[key] === \"string\",\n )\n : null);\n\n const totalRows = table.getFilteredRowModel().rows.length;\n const currentPage = table.getState().pagination.pageIndex + 1;\n const currentPageSize = table.getState().pagination.pageSize;\n const fromRow =\n totalRows === 0 ? 0 : (currentPage - 1) * currentPageSize + 1;\n const toRow = Math.min(currentPage * currentPageSize, totalRows);\n\n return (\n <div className=\"w-full\">\n <div className=\"flex items-center py-4 gap-2\">\n {defaultFilterColumn && (\n <Input\n placeholder={\n filterPlaceholder ||\n `Filter by ${formatFieldLabel(defaultFilterColumn)}...`\n }\n value={\n (table\n .getColumn(defaultFilterColumn)\n ?.getFilterValue() as string) ?? \"\"\n }\n onChange={(event) =>\n table\n .getColumn(defaultFilterColumn)\n ?.setFilterValue(event.target.value)\n }\n className=\"max-w-sm\"\n />\n )}\n <DropdownMenu>\n <DropdownMenuTrigger asChild>\n <Button variant=\"outline\" className=\"ml-auto\">\n {finalLabels.columnsButton}{\" \"}\n <ChevronDown className=\"ml-2 h-4 w-4\" />\n </Button>\n </DropdownMenuTrigger>\n <DropdownMenuContent align=\"end\">\n {table\n .getAllColumns()\n .filter((column) => column.getCanHide())\n .map((column) => {\n return (\n <DropdownMenuCheckboxItem\n key={column.id}\n className=\"capitalize\"\n checked={column.getIsVisible()}\n onCheckedChange={(value) =>\n column.toggleVisibility(!!value)\n }\n >\n {formatFieldLabel(column.id)}\n </DropdownMenuCheckboxItem>\n );\n })}\n </DropdownMenuContent>\n </DropdownMenu>\n </div>\n <div className=\"overflow-hidden rounded-md border border-border\">\n <div className=\"max-h-[600px] overflow-y-auto\">\n <Table>\n <TableHeader className=\"sticky top-0 bg-background z-10\">\n {table.getHeaderGroups().map((headerGroup) => (\n <TableRow key={headerGroup.id}>\n {headerGroup.headers.map((header) => {\n const isSelectColumn = header.column.id === \"select\";\n return (\n <TableHead\n key={header.id}\n style={{\n width: header.getSize(),\n position: \"relative\",\n }}\n className={\n isSelectColumn ? \"w-[40px] p-0\" : undefined\n }\n >\n {header.isPlaceholder\n ? null\n : flexRender(\n header.column.columnDef.header,\n header.getContext(),\n )}\n </TableHead>\n );\n })}\n </TableRow>\n ))}\n </TableHeader>\n <TableBody>\n {table.getRowModel().rows?.length ? (\n table.getRowModel().rows.map((row) => (\n <TableRow\n key={row.id}\n data-state={row.getIsSelected() && \"selected\"}\n >\n {row.getVisibleCells().map((cell) => {\n const isSelectColumn = cell.column.id === \"select\";\n return (\n <TableCell\n key={cell.id}\n style={{\n width: cell.column.getSize(),\n }}\n className={\n isSelectColumn ? \"w-[40px] p-0\" : undefined\n }\n >\n {flexRender(\n cell.column.columnDef.cell,\n cell.getContext(),\n )}\n </TableCell>\n );\n })}\n </TableRow>\n ))\n ) : (\n <TableRow>\n <TableCell\n colSpan={table.getAllColumns().length}\n className=\"h-24 text-center\"\n >\n {finalLabels.noResults}\n </TableCell>\n </TableRow>\n )}\n </TableBody>\n </Table>\n </div>\n </div>\n <div className=\"flex items-center justify-between py-4\">\n <div className=\"flex items-center gap-6\">\n <div className=\"text-foreground text-sm\">\n {totalRows > 0\n ? finalLabels.showing\n .replace(`\\${from}`, fromRow.toString())\n .replace(`\\${to}`, toRow.toString())\n .replace(`\\${total}`, totalRows.toString())\n : finalLabels.noResults}\n </div>\n <div className=\"flex items-center gap-2\">\n <span className=\"text-sm text-foreground\">\n {finalLabels.rowsPerPage}\n </span>\n <Select\n value={currentPageSize.toString()}\n onValueChange={(value) => {\n table.setPageSize(Number(value));\n }}\n >\n <SelectTrigger className=\"w-[70px]\">\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n {pageSizeOptions.map((size) => (\n <SelectItem key={size} value={size.toString()}>\n {size}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n </div>\n <div className=\"space-x-2\">\n <Button\n variant=\"outline\"\n size=\"sm\"\n onClick={() => table.previousPage()}\n disabled={!table.getCanPreviousPage()}\n >\n {finalLabels.previousButton}\n </Button>\n <Button\n variant=\"outline\"\n size=\"sm\"\n onClick={() => table.nextPage()}\n disabled={!table.getCanNextPage()}\n >\n {finalLabels.nextButton}\n </Button>\n </div>\n </div>\n </div>\n );\n }}\n </TableWrapper>\n );\n}\n"],"mappings":"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"}
@@ -0,0 +1,16 @@
1
+ import { jsx } from "react/jsx-runtime";
2
+
3
+ //#region src/react/table/empty.tsx
4
+ function EmptyState() {
5
+ return /* @__PURE__ */ jsx("div", {
6
+ className: "w-full p-8 text-center",
7
+ children: /* @__PURE__ */ jsx("p", {
8
+ className: "text-sm text-muted-foreground",
9
+ children: "No data available"
10
+ })
11
+ });
12
+ }
13
+
14
+ //#endregion
15
+ export { EmptyState };
16
+ //# sourceMappingURL=empty.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"empty.js","names":[],"sources":["../../../src/react/table/empty.tsx"],"sourcesContent":["export function EmptyState() {\n return (\n <div className=\"w-full p-8 text-center\">\n <p className=\"text-sm text-muted-foreground\">No data available</p>\n </div>\n );\n}\n"],"mappings":";;;AAAA,SAAgB,aAAa;AAC3B,QACE,oBAAC;EAAI,WAAU;YACb,oBAAC;GAAE,WAAU;aAAgC;IAAqB;GAC9D"}
@@ -0,0 +1,16 @@
1
+ import { jsx, jsxs } from "react/jsx-runtime";
2
+
3
+ //#region src/react/table/error.tsx
4
+ function ErrorState({ error }) {
5
+ return /* @__PURE__ */ jsx("div", {
6
+ className: "w-full p-8 text-center",
7
+ children: /* @__PURE__ */ jsxs("p", {
8
+ className: "text-sm text-destructive",
9
+ children: ["Error loading table: ", error]
10
+ })
11
+ });
12
+ }
13
+
14
+ //#endregion
15
+ export { ErrorState };
16
+ //# sourceMappingURL=error.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"error.js","names":[],"sources":["../../../src/react/table/error.tsx"],"sourcesContent":["export function ErrorState({ error }: { error: string }) {\n return (\n <div className=\"w-full p-8 text-center\">\n <p className=\"text-sm text-destructive\">Error loading table: {error}</p>\n </div>\n );\n}\n"],"mappings":";;;AAAA,SAAgB,WAAW,EAAE,SAA4B;AACvD,QACE,oBAAC;EAAI,WAAU;YACb,qBAAC;GAAE,WAAU;cAA2B,yBAAsB;IAAU;GACpE"}
@@ -0,0 +1 @@
1
+ import { DataTable } from "./data-table.js";
@@ -0,0 +1,50 @@
1
+ import { jsx, jsxs } from "react/jsx-runtime";
2
+
3
+ //#region src/react/table/loading.tsx
4
+ function LoadingSkeleton() {
5
+ return /* @__PURE__ */ jsxs("div", {
6
+ className: "w-full space-y-3 mt-2",
7
+ children: [
8
+ /* @__PURE__ */ jsxs("div", {
9
+ className: "flex items-center gap-2 mt-4",
10
+ children: [/* @__PURE__ */ jsx("div", { className: "h-10 bg-muted rounded animate-pulse flex-1 max-w-sm" }), /* @__PURE__ */ jsx("div", { className: "h-10 bg-muted rounded animate-pulse w-24 ml-auto" })]
11
+ }),
12
+ /* @__PURE__ */ jsxs("div", {
13
+ className: "rounded-md border overflow-hidden",
14
+ children: [/* @__PURE__ */ jsx("div", {
15
+ className: "border-b bg-secondary p-4",
16
+ children: /* @__PURE__ */ jsxs("div", {
17
+ className: "flex gap-4",
18
+ children: [
19
+ /* @__PURE__ */ jsx("div", { className: "h-8 bg-muted rounded animate-pulse flex-1" }),
20
+ /* @__PURE__ */ jsx("div", { className: "h-8 bg-muted rounded animate-pulse flex-1" }),
21
+ /* @__PURE__ */ jsx("div", { className: "h-8 bg-muted rounded animate-pulse flex-1" })
22
+ ]
23
+ })
24
+ }), Array.from({ length: 5 }).map(() => /* @__PURE__ */ jsx("div", {
25
+ className: "border-b p-4 last:border-b-0",
26
+ children: /* @__PURE__ */ jsxs("div", {
27
+ className: "flex gap-4",
28
+ children: [
29
+ /* @__PURE__ */ jsx("div", { className: "h-6 bg-muted rounded animate-pulse flex-1" }),
30
+ /* @__PURE__ */ jsx("div", { className: "h-6 bg-muted rounded animate-pulse flex-1" }),
31
+ /* @__PURE__ */ jsx("div", { className: "h-6 bg-muted rounded animate-pulse flex-1" })
32
+ ]
33
+ })
34
+ }, `loading-row-${Math.random()}`))]
35
+ }),
36
+ /* @__PURE__ */ jsxs("div", {
37
+ className: "flex items-center justify-end gap-2",
38
+ children: [
39
+ /* @__PURE__ */ jsx("div", { className: "h-4 bg-muted rounded animate-pulse w-32" }),
40
+ /* @__PURE__ */ jsx("div", { className: "h-9 bg-muted rounded animate-pulse w-20" }),
41
+ /* @__PURE__ */ jsx("div", { className: "h-9 bg-muted rounded animate-pulse w-20" })
42
+ ]
43
+ })
44
+ ]
45
+ });
46
+ }
47
+
48
+ //#endregion
49
+ export { LoadingSkeleton };
50
+ //# sourceMappingURL=loading.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"loading.js","names":[],"sources":["../../../src/react/table/loading.tsx"],"sourcesContent":["export function LoadingSkeleton() {\n return (\n <div className=\"w-full space-y-3 mt-2\">\n <div className=\"flex items-center gap-2 mt-4\">\n <div className=\"h-10 bg-muted rounded animate-pulse flex-1 max-w-sm\" />\n <div className=\"h-10 bg-muted rounded animate-pulse w-24 ml-auto\" />\n </div>\n\n <div className=\"rounded-md border overflow-hidden\">\n <div className=\"border-b bg-secondary p-4\">\n <div className=\"flex gap-4\">\n <div className=\"h-8 bg-muted rounded animate-pulse flex-1\" />\n <div className=\"h-8 bg-muted rounded animate-pulse flex-1\" />\n <div className=\"h-8 bg-muted rounded animate-pulse flex-1\" />\n </div>\n </div>\n\n {Array.from({ length: 5 }).map(() => (\n <div\n key={`loading-row-${Math.random()}`}\n className=\"border-b p-4 last:border-b-0\"\n >\n <div className=\"flex gap-4\">\n <div className=\"h-6 bg-muted rounded animate-pulse flex-1\" />\n <div className=\"h-6 bg-muted rounded animate-pulse flex-1\" />\n <div className=\"h-6 bg-muted rounded animate-pulse flex-1\" />\n </div>\n </div>\n ))}\n </div>\n\n <div className=\"flex items-center justify-end gap-2\">\n <div className=\"h-4 bg-muted rounded animate-pulse w-32\" />\n <div className=\"h-9 bg-muted rounded animate-pulse w-20\" />\n <div className=\"h-9 bg-muted rounded animate-pulse w-20\" />\n </div>\n </div>\n );\n}\n"],"mappings":";;;AAAA,SAAgB,kBAAkB;AAChC,QACE,qBAAC;EAAI,WAAU;;GACb,qBAAC;IAAI,WAAU;eACb,oBAAC,SAAI,WAAU,wDAAwD,EACvE,oBAAC,SAAI,WAAU,qDAAqD;KAChE;GAEN,qBAAC;IAAI,WAAU;eACb,oBAAC;KAAI,WAAU;eACb,qBAAC;MAAI,WAAU;;OACb,oBAAC,SAAI,WAAU,8CAA8C;OAC7D,oBAAC,SAAI,WAAU,8CAA8C;OAC7D,oBAAC,SAAI,WAAU,8CAA8C;;OACzD;MACF,EAEL,MAAM,KAAK,EAAE,QAAQ,GAAG,CAAC,CAAC,UACzB,oBAAC;KAEC,WAAU;eAEV,qBAAC;MAAI,WAAU;;OACb,oBAAC,SAAI,WAAU,8CAA8C;OAC7D,oBAAC,SAAI,WAAU,8CAA8C;OAC7D,oBAAC,SAAI,WAAU,8CAA8C;;OACzD;OAPD,eAAe,KAAK,QAAQ,GAQ7B,CACN;KACE;GAEN,qBAAC;IAAI,WAAU;;KACb,oBAAC,SAAI,WAAU,4CAA4C;KAC3D,oBAAC,SAAI,WAAU,4CAA4C;KAC3D,oBAAC,SAAI,WAAU,4CAA4C;;KACvD;;GACF"}
@@ -0,0 +1,143 @@
1
+ import { useAnalyticsQuery } from "../hooks/use-analytics-query.js";
2
+ import { SAFE_KEY_REGEX, formatChartValue, formatFieldLabel } from "../lib/format.js";
3
+ import { Button } from "../ui/button.js";
4
+ import { Checkbox } from "../ui/checkbox.js";
5
+ import { EmptyState } from "./empty.js";
6
+ import { ErrorState } from "./error.js";
7
+ import { LoadingSkeleton } from "./loading.js";
8
+ import "../index.js";
9
+ import { useEffect, useMemo, useState } from "react";
10
+ import { jsx, jsxs } from "react/jsx-runtime";
11
+ import { getCoreRowModel, getFilteredRowModel, getPaginationRowModel, getSortedRowModel, useReactTable } from "@tanstack/react-table";
12
+ import { ArrowUpDown } from "lucide-react";
13
+
14
+ //#region src/react/table/table-wrapper.tsx
15
+ const CHECKBOX_COLUMN_WIDTH = 40;
16
+ /**
17
+ * Wrapper component for tables with automatic data fetching and state management
18
+ * This component handles:
19
+ * - Data fetching via useAnalyticsQuery
20
+ * - Loading, error, and empty states with proper UI components
21
+ * - Data transformation (optional)
22
+ * - Dynamic column generation from data structure
23
+ * - TanStack Table instance creation with all features (sorting, filtering, pagination, etc.)
24
+ *
25
+ * @template TRaw - The raw data type returned by the analytics query
26
+ * @template TProcessed - The processed data type after transformation
27
+ *
28
+ * @param props - Props for the TableWrapper component
29
+ * @param props.queryKey - The query key to fetch the data
30
+ * @param props.parameters - The parameters to pass to the query
31
+ * @param props.transformer - Optional function to transform raw data before creating table
32
+ * @param props.children - Render function that receives the TanStack Table instance
33
+ * @param props.className - Optional CSS class name for the wrapper
34
+ * @param props.ariaLabel - Optional accessibility label
35
+ * @param props.testId - Optional test ID for testing
36
+ *
37
+ * @returns The rendered table with state management
38
+ */
39
+ function TableWrapper(props) {
40
+ const { queryKey, parameters, transformer, children, className, ariaLabel, testId, enableRowSelection = false, onRowSelectionChange, pageSize = 10 } = props;
41
+ const [sorting, setSorting] = useState([]);
42
+ const [columnFilters, setColumnFilters] = useState([]);
43
+ const [columnVisibility, setColumnVisibility] = useState({});
44
+ const [rowSelection, setRowSelection] = useState({});
45
+ const { data, loading, error } = useAnalyticsQuery(queryKey, parameters);
46
+ useEffect(() => {
47
+ if (onRowSelectionChange && enableRowSelection) onRowSelectionChange(rowSelection);
48
+ }, [
49
+ rowSelection,
50
+ onRowSelectionChange,
51
+ enableRowSelection
52
+ ]);
53
+ const hasData = data && data.length > 0;
54
+ const processedData = hasData ? transformer ? transformer(data) : data : [];
55
+ const table = useReactTable({
56
+ data: processedData,
57
+ columns: useMemo(() => {
58
+ if (!hasData) return [];
59
+ if (!processedData[0] || typeof processedData[0] !== "object") {
60
+ console.warn("Invalid data format for DataTable");
61
+ return [];
62
+ }
63
+ const dataColumns = Object.keys(processedData[0]).filter((key) => SAFE_KEY_REGEX.test(key)).map((key) => {
64
+ const formattedLabel = formatFieldLabel(key);
65
+ return {
66
+ accessorKey: key,
67
+ header: ({ column }) => {
68
+ return /* @__PURE__ */ jsxs(Button, {
69
+ variant: "ghost",
70
+ onClick: () => column.toggleSorting(column.getIsSorted() === "asc"),
71
+ className: "h-8 px-2",
72
+ children: [formattedLabel, /* @__PURE__ */ jsx(ArrowUpDown, { className: "ml-2 h-4 w-4" })]
73
+ });
74
+ },
75
+ cell: ({ row }) => {
76
+ const value = row.getValue(key);
77
+ if (typeof value === "number" || Number.isFinite(Number(value))) return /* @__PURE__ */ jsx("div", {
78
+ className: "text-right font-mono",
79
+ children: formatChartValue(Number(value), key)
80
+ });
81
+ return /* @__PURE__ */ jsx("div", { children: String(value) });
82
+ }
83
+ };
84
+ });
85
+ if (enableRowSelection) return [{
86
+ id: "select",
87
+ maxSize: CHECKBOX_COLUMN_WIDTH,
88
+ minSize: CHECKBOX_COLUMN_WIDTH,
89
+ header: ({ table: table$1 }) => /* @__PURE__ */ jsx("div", {
90
+ className: "flex items-center justify-center",
91
+ children: /* @__PURE__ */ jsx(Checkbox, {
92
+ checked: table$1.getIsAllPageRowsSelected() || table$1.getIsSomePageRowsSelected() && "indeterminate",
93
+ onCheckedChange: (value) => table$1.toggleAllPageRowsSelected(!!value),
94
+ "aria-label": "Select all"
95
+ })
96
+ }),
97
+ cell: ({ row }) => /* @__PURE__ */ jsx("div", {
98
+ className: "flex items-center justify-center",
99
+ children: /* @__PURE__ */ jsx(Checkbox, {
100
+ checked: row.getIsSelected(),
101
+ onCheckedChange: (value) => row.toggleSelected(!!value),
102
+ "aria-label": "Select row"
103
+ })
104
+ }),
105
+ enableSorting: false,
106
+ enableHiding: false
107
+ }, ...dataColumns];
108
+ return dataColumns;
109
+ }, [
110
+ hasData,
111
+ processedData,
112
+ enableRowSelection
113
+ ]),
114
+ onSortingChange: setSorting,
115
+ onColumnFiltersChange: setColumnFilters,
116
+ getCoreRowModel: getCoreRowModel(),
117
+ getPaginationRowModel: getPaginationRowModel(),
118
+ getSortedRowModel: getSortedRowModel(),
119
+ getFilteredRowModel: getFilteredRowModel(),
120
+ onColumnVisibilityChange: setColumnVisibility,
121
+ onRowSelectionChange: setRowSelection,
122
+ state: {
123
+ sorting,
124
+ columnFilters,
125
+ columnVisibility,
126
+ rowSelection
127
+ },
128
+ initialState: { pagination: { pageSize } }
129
+ });
130
+ if (loading) return /* @__PURE__ */ jsx(LoadingSkeleton, {});
131
+ if (error) return /* @__PURE__ */ jsx(ErrorState, { error: typeof error === "string" ? error : "Unknown error" });
132
+ if (!hasData) return /* @__PURE__ */ jsx(EmptyState, {});
133
+ return /* @__PURE__ */ jsx("section", {
134
+ className,
135
+ "aria-label": ariaLabel,
136
+ "data-testid": testId,
137
+ children: children(table)
138
+ });
139
+ }
140
+
141
+ //#endregion
142
+ export { TableWrapper };
143
+ //# sourceMappingURL=table-wrapper.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"table-wrapper.js","names":["table"],"sources":["../../../src/react/table/table-wrapper.tsx"],"sourcesContent":["import {\n type Column,\n type ColumnFiltersState,\n getCoreRowModel,\n getFilteredRowModel,\n getPaginationRowModel,\n getSortedRowModel,\n type Row,\n type RowSelectionState,\n type SortingState,\n type Table,\n useReactTable,\n type VisibilityState,\n} from \"@tanstack/react-table\";\nimport { ArrowUpDown } from \"lucide-react\";\nimport { useEffect, useMemo, useState } from \"react\";\nimport { useAnalyticsQuery } from \"..\";\nimport {\n formatChartValue,\n formatFieldLabel,\n SAFE_KEY_REGEX,\n} from \"../lib/format\";\nimport { Button } from \"../ui/button\";\nimport { Checkbox } from \"../ui/checkbox\";\nimport { EmptyState } from \"./empty\";\nimport { ErrorState } from \"./error\";\nimport { LoadingSkeleton } from \"./loading\";\nimport type { TableWrapperProps } from \"./types\";\n\nconst CHECKBOX_COLUMN_WIDTH = 40;\n\n/**\n * Wrapper component for tables with automatic data fetching and state management\n * This component handles:\n * - Data fetching via useAnalyticsQuery\n * - Loading, error, and empty states with proper UI components\n * - Data transformation (optional)\n * - Dynamic column generation from data structure\n * - TanStack Table instance creation with all features (sorting, filtering, pagination, etc.)\n *\n * @template TRaw - The raw data type returned by the analytics query\n * @template TProcessed - The processed data type after transformation\n *\n * @param props - Props for the TableWrapper component\n * @param props.queryKey - The query key to fetch the data\n * @param props.parameters - The parameters to pass to the query\n * @param props.transformer - Optional function to transform raw data before creating table\n * @param props.children - Render function that receives the TanStack Table instance\n * @param props.className - Optional CSS class name for the wrapper\n * @param props.ariaLabel - Optional accessibility label\n * @param props.testId - Optional test ID for testing\n *\n * @returns The rendered table with state management\n */\nexport function TableWrapper<TRaw = any, TProcessed = any>(\n props: TableWrapperProps<TRaw, TProcessed>,\n) {\n const {\n queryKey,\n parameters,\n transformer,\n children,\n className,\n ariaLabel,\n testId,\n enableRowSelection = false,\n onRowSelectionChange,\n pageSize = 10,\n } = props;\n\n const [sorting, setSorting] = useState<SortingState>([]);\n const [columnFilters, setColumnFilters] = useState<ColumnFiltersState>([]);\n const [columnVisibility, setColumnVisibility] = useState<VisibilityState>({});\n const [rowSelection, setRowSelection] = useState<RowSelectionState>({});\n\n const { data, loading, error } = useAnalyticsQuery<TRaw[]>(\n queryKey,\n parameters,\n );\n\n useEffect(() => {\n if (onRowSelectionChange && enableRowSelection) {\n onRowSelectionChange(rowSelection);\n }\n }, [rowSelection, onRowSelectionChange, enableRowSelection]);\n\n const hasData = data && data.length > 0;\n\n const processedData = hasData\n ? transformer\n ? transformer(data)\n : (data as unknown as TProcessed[])\n : [];\n\n const tableColumns = useMemo(() => {\n if (!hasData) return [];\n\n if (!processedData[0] || typeof processedData[0] !== \"object\") {\n console.warn(\"Invalid data format for DataTable\");\n return [];\n }\n\n const dataColumns = Object.keys(processedData[0] as object)\n .filter((key) => SAFE_KEY_REGEX.test(key))\n .map((key) => {\n const formattedLabel = formatFieldLabel(key);\n return {\n accessorKey: key,\n header: ({ column }: { column: Column<TProcessed> }) => {\n return (\n <Button\n variant=\"ghost\"\n onClick={() =>\n column.toggleSorting(column.getIsSorted() === \"asc\")\n }\n className=\"h-8 px-2\"\n >\n {formattedLabel}\n <ArrowUpDown className=\"ml-2 h-4 w-4\" />\n </Button>\n );\n },\n cell: ({ row }: { row: Row<TProcessed> }) => {\n const value = row.getValue(key);\n if (typeof value === \"number\" || Number.isFinite(Number(value))) {\n return (\n <div className=\"text-right font-mono\">\n {formatChartValue(Number(value), key)}\n </div>\n );\n }\n return <div>{String(value)}</div>;\n },\n };\n });\n\n if (enableRowSelection) {\n return [\n {\n id: \"select\",\n maxSize: CHECKBOX_COLUMN_WIDTH,\n minSize: CHECKBOX_COLUMN_WIDTH,\n header: ({ table }: { table: Table<TProcessed> }) => (\n <div className=\"flex items-center justify-center\">\n <Checkbox\n checked={\n table.getIsAllPageRowsSelected() ||\n (table.getIsSomePageRowsSelected() && \"indeterminate\")\n }\n onCheckedChange={(value) =>\n table.toggleAllPageRowsSelected(!!value)\n }\n aria-label=\"Select all\"\n />\n </div>\n ),\n cell: ({ row }: { row: Row<TProcessed> }) => (\n <div className=\"flex items-center justify-center\">\n <Checkbox\n checked={row.getIsSelected()}\n onCheckedChange={(value) => row.toggleSelected(!!value)}\n aria-label=\"Select row\"\n />\n </div>\n ),\n enableSorting: false,\n enableHiding: false,\n },\n ...dataColumns,\n ];\n }\n\n return dataColumns;\n }, [hasData, processedData, enableRowSelection]);\n\n const table = useReactTable({\n data: processedData,\n columns: tableColumns,\n onSortingChange: setSorting,\n onColumnFiltersChange: setColumnFilters,\n getCoreRowModel: getCoreRowModel(),\n getPaginationRowModel: getPaginationRowModel(),\n getSortedRowModel: getSortedRowModel(),\n getFilteredRowModel: getFilteredRowModel(),\n onColumnVisibilityChange: setColumnVisibility,\n onRowSelectionChange: setRowSelection,\n state: {\n sorting,\n columnFilters,\n columnVisibility,\n rowSelection,\n },\n initialState: {\n pagination: {\n pageSize: pageSize,\n },\n },\n });\n\n if (loading) return <LoadingSkeleton />;\n if (error)\n return (\n <ErrorState error={typeof error === \"string\" ? error : \"Unknown error\"} />\n );\n\n if (!hasData) return <EmptyState />;\n\n return (\n <section className={className} aria-label={ariaLabel} data-testid={testId}>\n {children(table)}\n </section>\n );\n}\n"],"mappings":"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"}