@cosmicdrift/kumiko-renderer 0.388.0 → 0.390.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 (379) hide show
  1. package/dist/app/base-path-context.d.ts +9 -0
  2. package/dist/app/base-path-context.js +13 -0
  3. package/dist/app/base-path.d.ts +3 -0
  4. package/dist/app/base-path.js +27 -0
  5. package/dist/app/config-edit-body.d.ts +1 -1
  6. package/dist/app/config-edit-body.js +15 -6
  7. package/dist/app/config-edit-without-secret-fields.d.ts +2 -0
  8. package/dist/app/config-edit-without-secret-fields.js +28 -0
  9. package/dist/app/entity-list-facet-specs.d.ts +3 -0
  10. package/dist/app/entity-list-facet-specs.js +65 -0
  11. package/dist/app/entity-list-screen.js +74 -206
  12. package/dist/app/kumiko-screen.js +6 -14
  13. package/dist/app/list-bulk-actions.d.ts +18 -0
  14. package/dist/app/list-bulk-actions.js +54 -0
  15. package/dist/app/locked-screen-header.d.ts +9 -0
  16. package/dist/app/locked-screen-header.js +33 -0
  17. package/dist/app/projection-detail-body.js +59 -394
  18. package/dist/app/projection-detail-header-model.d.ts +11 -0
  19. package/dist/app/projection-detail-header-model.js +33 -0
  20. package/dist/app/projection-detail-header-region.d.ts +28 -0
  21. package/dist/app/projection-detail-header-region.js +81 -0
  22. package/dist/app/projection-detail-header.d.ts +29 -0
  23. package/dist/app/projection-detail-header.js +84 -0
  24. package/dist/app/projection-detail-navigation.d.ts +13 -0
  25. package/dist/app/projection-detail-navigation.js +91 -0
  26. package/dist/app/projection-detail-states.d.ts +22 -0
  27. package/dist/app/projection-detail-states.js +26 -0
  28. package/dist/app/projection-drawer-host.d.ts +12 -0
  29. package/dist/app/projection-drawer-host.js +10 -0
  30. package/dist/app/projection-list-body.d.ts +1 -1
  31. package/dist/app/projection-list-body.js +82 -197
  32. package/dist/app/projection-list-render-props.d.ts +56 -0
  33. package/dist/app/projection-list-render-props.js +47 -0
  34. package/dist/app/projection-list-render.d.ts +26 -0
  35. package/dist/app/projection-list-render.js +20 -0
  36. package/dist/app/projection-list-row-click.d.ts +12 -0
  37. package/dist/app/projection-list-row-click.js +14 -0
  38. package/dist/app/row-actions.d.ts +3 -0
  39. package/dist/app/row-actions.js +10 -5
  40. package/dist/app/screen-shared.js +9 -2
  41. package/dist/app/use-entity-range-facets.d.ts +24 -0
  42. package/dist/app/use-entity-range-facets.js +64 -0
  43. package/dist/app/use-projection-date-range.d.ts +12 -0
  44. package/dist/app/use-projection-date-range.js +21 -0
  45. package/dist/app/use-projection-detail-actions.d.ts +35 -0
  46. package/dist/app/use-projection-detail-actions.js +156 -0
  47. package/dist/app/use-projection-detail-data.d.ts +19 -0
  48. package/dist/app/use-projection-detail-data.js +47 -0
  49. package/dist/app/use-projection-detail-layout.d.ts +17 -0
  50. package/dist/app/use-projection-detail-layout.js +23 -0
  51. package/dist/app/use-projection-list-actions.d.ts +31 -0
  52. package/dist/app/use-projection-list-actions.js +73 -0
  53. package/dist/app/use-projection-list-data.d.ts +20 -0
  54. package/dist/app/use-projection-list-data.js +26 -0
  55. package/dist/app/use-projection-list-definitions.d.ts +11 -0
  56. package/dist/app/use-projection-list-definitions.js +20 -0
  57. package/dist/app/use-projection-list-query.d.ts +28 -0
  58. package/dist/app/use-projection-list-query.js +83 -0
  59. package/dist/app/use-screen-list-rows.d.ts +37 -0
  60. package/dist/app/use-screen-list-rows.js +104 -0
  61. package/dist/app/use-screen-list-table.d.ts +24 -0
  62. package/dist/app/use-screen-list-table.js +46 -0
  63. package/dist/components/list-group-page-bridges.d.ts +3 -0
  64. package/dist/components/list-group-page-bridges.js +31 -0
  65. package/dist/components/list-state-store.d.ts +16 -0
  66. package/dist/components/list-state-store.js +31 -0
  67. package/dist/components/related-list-section.js +37 -89
  68. package/dist/components/render-edit-banners.d.ts +12 -0
  69. package/dist/components/render-edit-banners.js +90 -0
  70. package/dist/components/render-edit-draft-save-helpers.d.ts +19 -0
  71. package/dist/components/render-edit-draft-save-helpers.js +42 -0
  72. package/dist/components/render-edit-field-grid.d.ts +4 -0
  73. package/dist/components/render-edit-field-grid.js +40 -0
  74. package/dist/components/render-edit-fields-section.d.ts +4 -0
  75. package/dist/components/render-edit-fields-section.js +76 -0
  76. package/dist/components/render-edit-form-actions.d.ts +4 -0
  77. package/dist/components/render-edit-form-actions.js +66 -0
  78. package/dist/components/render-edit-header-actions.d.ts +11 -0
  79. package/dist/components/render-edit-header-actions.js +120 -0
  80. package/dist/components/render-edit-layout-flags.d.ts +29 -0
  81. package/dist/components/render-edit-layout-flags.js +106 -0
  82. package/dist/components/render-edit-layout.d.ts +4 -0
  83. package/dist/components/render-edit-layout.js +79 -0
  84. package/dist/components/render-edit-render-model.d.ts +63 -0
  85. package/dist/components/render-edit-render-model.js +14 -0
  86. package/dist/components/render-edit-section-kinds.d.ts +4 -0
  87. package/dist/components/render-edit-section-kinds.js +115 -0
  88. package/dist/components/render-edit-submit-result.d.ts +8 -0
  89. package/dist/components/render-edit-submit-result.js +77 -0
  90. package/dist/components/render-edit-submit.d.ts +7 -0
  91. package/dist/components/render-edit-submit.js +113 -0
  92. package/dist/components/render-edit-titles.d.ts +17 -0
  93. package/dist/components/render-edit-titles.js +99 -0
  94. package/dist/components/render-edit-wizard-navigation.d.ts +11 -0
  95. package/dist/components/render-edit-wizard-navigation.js +115 -0
  96. package/dist/components/render-edit.d.ts +2 -2
  97. package/dist/components/render-edit.js +63 -1128
  98. package/dist/components/render-field-choice-inputs.d.ts +7 -0
  99. package/dist/components/render-field-choice-inputs.js +52 -0
  100. package/dist/components/render-field-control.d.ts +12 -0
  101. package/dist/components/render-field-control.js +93 -0
  102. package/dist/components/render-field-display-text.d.ts +4 -0
  103. package/dist/components/render-field-display-text.js +48 -0
  104. package/dist/components/render-field-fill-action.d.ts +13 -0
  105. package/dist/components/render-field-fill-action.js +63 -0
  106. package/dist/components/render-field-input-shared.d.ts +39 -0
  107. package/dist/components/render-field-input-shared.js +20 -0
  108. package/dist/components/render-field-input.d.ts +3 -0
  109. package/dist/components/render-field-input.js +34 -0
  110. package/dist/components/render-field-meta.d.ts +6 -0
  111. package/dist/components/render-field-meta.js +51 -0
  112. package/dist/components/render-field-read-only.d.ts +11 -0
  113. package/dist/components/render-field-read-only.js +75 -0
  114. package/dist/components/render-field-reference.d.ts +15 -0
  115. package/dist/components/render-field-reference.js +103 -0
  116. package/dist/components/render-field-scalar-inputs.d.ts +10 -0
  117. package/dist/components/render-field-scalar-inputs.js +35 -0
  118. package/dist/components/render-field-text-inputs.d.ts +3 -0
  119. package/dist/components/render-field-text-inputs.js +34 -0
  120. package/dist/components/render-field-values.d.ts +18 -0
  121. package/dist/components/render-field-values.js +111 -0
  122. package/dist/components/render-field-write-only.d.ts +9 -0
  123. package/dist/components/render-field-write-only.js +29 -0
  124. package/dist/components/render-field.d.ts +2 -2
  125. package/dist/components/render-field.js +39 -669
  126. package/dist/components/render-list-header-slot.d.ts +5 -0
  127. package/dist/components/render-list-header-slot.js +30 -0
  128. package/dist/components/render-list-reference-bridge.d.ts +3 -0
  129. package/dist/components/render-list-reference-bridge.js +25 -0
  130. package/dist/components/render-list-reference-columns.d.ts +25 -0
  131. package/dist/components/render-list-reference-columns.js +137 -0
  132. package/dist/components/render-list-table-props.d.ts +52 -0
  133. package/dist/components/render-list-table-props.js +53 -0
  134. package/dist/components/render-list-toolbar-action.d.ts +27 -0
  135. package/dist/components/render-list-toolbar-action.js +40 -0
  136. package/dist/components/render-list-toolbar-model.d.ts +42 -0
  137. package/dist/components/render-list-toolbar-model.js +82 -0
  138. package/dist/components/render-list-toolbar.d.ts +38 -0
  139. package/dist/components/render-list-toolbar.js +40 -0
  140. package/dist/components/render-list-types.d.ts +113 -0
  141. package/dist/components/render-list-types.js +1 -0
  142. package/dist/components/render-list.d.ts +5 -130
  143. package/dist/components/render-list.js +95 -319
  144. package/dist/components/use-reference-input-state.d.ts +20 -0
  145. package/dist/components/use-reference-input-state.js +85 -0
  146. package/dist/components/use-render-edit-controlled-sync.d.ts +3 -0
  147. package/dist/components/use-render-edit-controlled-sync.js +26 -0
  148. package/dist/components/use-render-edit-draft-save.d.ts +29 -0
  149. package/dist/components/use-render-edit-draft-save.js +141 -0
  150. package/dist/components/use-render-edit-form.d.ts +10 -0
  151. package/dist/components/use-render-edit-form.js +101 -0
  152. package/dist/components/use-render-edit-sections.d.ts +15 -0
  153. package/dist/components/use-render-edit-sections.js +100 -0
  154. package/dist/components/use-render-edit-state.d.ts +65 -0
  155. package/dist/components/use-render-edit-state.js +158 -0
  156. package/dist/components/use-render-edit-steps.d.ts +13 -0
  157. package/dist/components/use-render-edit-steps.js +52 -0
  158. package/dist/components/use-render-list-columns.d.ts +18 -0
  159. package/dist/components/use-render-list-columns.js +41 -0
  160. package/dist/components/use-render-list-search.d.ts +4 -0
  161. package/dist/components/use-render-list-search.js +50 -0
  162. package/dist/current-user.d.ts +2 -0
  163. package/dist/data-table-aggregate-types.d.ts +47 -0
  164. package/dist/data-table-aggregate-types.js +1 -0
  165. package/dist/data-table-bulk-types.d.ts +27 -0
  166. package/dist/data-table-bulk-types.js +1 -0
  167. package/dist/data-table-facet-types.d.ts +36 -0
  168. package/dist/data-table-facet-types.js +1 -0
  169. package/dist/data-table-view-types.d.ts +73 -0
  170. package/dist/data-table-view-types.js +1 -0
  171. package/dist/headless/view-model/edit.js +3 -1
  172. package/dist/headless/view-model/types.d.ts +4 -2
  173. package/dist/hooks/list-column-layout.d.ts +19 -0
  174. package/dist/hooks/list-column-layout.js +36 -0
  175. package/dist/hooks/list-group-pages.d.ts +26 -0
  176. package/dist/hooks/list-group-pages.js +37 -0
  177. package/dist/hooks/list-reset.d.ts +8 -0
  178. package/dist/hooks/list-reset.js +15 -0
  179. package/dist/hooks/list-server-groups.d.ts +39 -0
  180. package/dist/hooks/list-server-groups.js +84 -0
  181. package/dist/hooks/list-state-schema.d.ts +28 -0
  182. package/dist/hooks/list-state-schema.js +76 -0
  183. package/dist/hooks/use-bulk-run.d.ts +41 -0
  184. package/dist/hooks/use-bulk-run.js +65 -0
  185. package/dist/hooks/use-group-load-state.d.ts +11 -0
  186. package/dist/hooks/use-group-load-state.js +31 -0
  187. package/dist/hooks/use-list-aggregates.d.ts +38 -0
  188. package/dist/hooks/use-list-aggregates.js +107 -0
  189. package/dist/hooks/use-list-table.d.ts +38 -0
  190. package/dist/hooks/use-list-table.js +91 -0
  191. package/dist/hooks/use-list-view-state.d.ts +43 -0
  192. package/dist/hooks/use-list-view-state.js +161 -0
  193. package/dist/hooks/use-related-list-rows.d.ts +27 -0
  194. package/dist/hooks/use-related-list-rows.js +81 -0
  195. package/dist/hooks/use-related-list-state.d.ts +15 -0
  196. package/dist/hooks/use-related-list-state.js +31 -0
  197. package/dist/hooks/use-remembered-list-params.d.ts +13 -0
  198. package/dist/hooks/use-remembered-list-params.js +53 -0
  199. package/dist/hooks/use-remembered-local-list-state.d.ts +26 -0
  200. package/dist/hooks/use-remembered-local-list-state.js +63 -0
  201. package/dist/i18n-defaults.js +53 -0
  202. package/dist/index.d.ts +3 -1
  203. package/dist/index.js +2 -0
  204. package/dist/primitives.d.ts +11 -47
  205. package/dist/web/app/create-app.d.ts +5 -0
  206. package/dist/web/app/create-app.js +7 -4
  207. package/dist/web/app/dashboard-body.d.ts +1 -1
  208. package/dist/web/app/dashboard-body.js +42 -246
  209. package/dist/web/app/dashboard-header.d.ts +34 -0
  210. package/dist/web/app/dashboard-header.js +58 -0
  211. package/dist/web/app/dashboard-screen-controls.d.ts +11 -0
  212. package/dist/web/app/dashboard-screen-controls.js +42 -0
  213. package/dist/web/app/dashboard-screen-tiles.d.ts +8 -0
  214. package/dist/web/app/dashboard-screen-tiles.js +42 -0
  215. package/dist/web/app/dashboard-visibility.d.ts +42 -0
  216. package/dist/web/app/dashboard-visibility.js +102 -0
  217. package/dist/web/app/history-leave-guard.d.ts +6 -0
  218. package/dist/web/app/history-leave-guard.js +115 -0
  219. package/dist/web/app/list-state-store-boot.d.ts +6 -0
  220. package/dist/web/app/list-state-store-boot.js +15 -0
  221. package/dist/web/app/nav.js +7 -54
  222. package/dist/web/app/session-list-state-store.d.ts +15 -0
  223. package/dist/web/app/session-list-state-store.js +117 -0
  224. package/dist/web/app/use-dashboard-screen-state.d.ts +35 -0
  225. package/dist/web/app/use-dashboard-screen-state.js +69 -0
  226. package/dist/web/index.d.ts +1 -0
  227. package/dist/web/index.js +1 -0
  228. package/dist/web/lib/export-xlsx-request.d.ts +11 -0
  229. package/dist/web/lib/export-xlsx-request.js +46 -0
  230. package/dist/web/primitives/data-table/actions-column.d.ts +16 -0
  231. package/dist/web/primitives/data-table/actions-column.js +35 -0
  232. package/dist/web/primitives/data-table/bulk-selection-bar.d.ts +20 -0
  233. package/dist/web/primitives/data-table/bulk-selection-bar.js +37 -0
  234. package/dist/web/primitives/data-table/card-body.d.ts +6 -0
  235. package/dist/web/primitives/data-table/card-body.js +32 -0
  236. package/dist/web/primitives/data-table/card-row.d.ts +5 -0
  237. package/dist/web/primitives/data-table/card-row.js +31 -0
  238. package/dist/web/primitives/data-table/card-sort-select.d.ts +14 -0
  239. package/dist/web/primitives/data-table/card-sort-select.js +40 -0
  240. package/dist/web/primitives/data-table/card-view.d.ts +3 -0
  241. package/dist/web/primitives/data-table/card-view.js +13 -0
  242. package/dist/web/primitives/data-table/column-layout-mode.d.ts +5 -0
  243. package/dist/web/primitives/data-table/column-layout-mode.js +10 -0
  244. package/dist/web/primitives/data-table/column-picker-section.d.ts +7 -0
  245. package/dist/web/primitives/data-table/column-picker-section.js +35 -0
  246. package/dist/web/primitives/data-table/column-resize-handle.d.ts +14 -0
  247. package/dist/web/primitives/data-table/column-resize-handle.js +48 -0
  248. package/dist/web/primitives/data-table/data-cell-props.d.ts +7 -0
  249. package/dist/web/primitives/data-table/data-cell-props.js +8 -0
  250. package/dist/web/primitives/data-table/density.d.ts +14 -0
  251. package/dist/web/primitives/data-table/density.js +20 -0
  252. package/dist/web/primitives/data-table/export-button.d.ts +5 -0
  253. package/dist/web/primitives/data-table/export-button.js +37 -0
  254. package/dist/web/primitives/data-table/facet-cluster.d.ts +8 -0
  255. package/dist/web/primitives/data-table/facet-cluster.js +37 -0
  256. package/dist/web/primitives/data-table/group-band.d.ts +28 -0
  257. package/dist/web/primitives/data-table/group-band.js +53 -0
  258. package/dist/web/primitives/data-table/measure-value.d.ts +10 -0
  259. package/dist/web/primitives/data-table/measure-value.js +28 -0
  260. package/dist/web/primitives/data-table/pin-section.d.ts +8 -0
  261. package/dist/web/primitives/data-table/pin-section.js +20 -0
  262. package/dist/web/primitives/data-table/reset-all-button.d.ts +4 -0
  263. package/dist/web/primitives/data-table/reset-all-button.js +8 -0
  264. package/dist/web/primitives/data-table/row-render-parts.d.ts +8 -0
  265. package/dist/web/primitives/data-table/row-render-parts.js +35 -0
  266. package/dist/web/primitives/data-table/server-group-header.d.ts +11 -0
  267. package/dist/web/primitives/data-table/server-group-header.js +51 -0
  268. package/dist/web/primitives/data-table/sortable-header.d.ts +17 -0
  269. package/dist/web/primitives/data-table/sortable-header.js +41 -0
  270. package/dist/web/primitives/data-table/table-body.d.ts +3 -0
  271. package/dist/web/primitives/data-table/table-body.js +32 -0
  272. package/dist/web/primitives/data-table/table-chrome.d.ts +13 -0
  273. package/dist/web/primitives/data-table/table-chrome.js +30 -0
  274. package/dist/web/primitives/data-table/table-content.d.ts +3 -0
  275. package/dist/web/primitives/data-table/table-content.js +41 -0
  276. package/dist/web/primitives/data-table/table-data-cell.d.ts +4 -0
  277. package/dist/web/primitives/data-table/table-data-cell.js +25 -0
  278. package/dist/web/primitives/data-table/table-footer.d.ts +7 -0
  279. package/dist/web/primitives/data-table/table-footer.js +20 -0
  280. package/dist/web/primitives/data-table/table-header-row.d.ts +3 -0
  281. package/dist/web/primitives/data-table/table-header-row.js +21 -0
  282. package/dist/web/primitives/data-table/table-view.d.ts +3 -0
  283. package/dist/web/primitives/data-table/table-view.js +25 -0
  284. package/dist/web/primitives/data-table/toolbar-end-cluster.d.ts +10 -0
  285. package/dist/web/primitives/data-table/toolbar-end-cluster.js +13 -0
  286. package/dist/web/primitives/data-table/toolbar.d.ts +6 -0
  287. package/dist/web/primitives/data-table/toolbar.js +53 -0
  288. package/dist/web/primitives/data-table/totals-footer.d.ts +24 -0
  289. package/dist/web/primitives/data-table/totals-footer.js +48 -0
  290. package/dist/web/primitives/data-table/use-bulk-selection.d.ts +21 -0
  291. package/dist/web/primitives/data-table/use-bulk-selection.js +66 -0
  292. package/dist/web/primitives/data-table/use-column-pins.d.ts +32 -0
  293. package/dist/web/primitives/data-table/use-column-pins.js +117 -0
  294. package/dist/web/primitives/data-table/use-column-sizing.d.ts +22 -0
  295. package/dist/web/primitives/data-table/use-column-sizing.js +44 -0
  296. package/dist/web/primitives/data-table/use-data-table-state.d.ts +44 -0
  297. package/dist/web/primitives/data-table/use-data-table-state.js +123 -0
  298. package/dist/web/primitives/data-table/use-measured-column-widths.d.ts +15 -0
  299. package/dist/web/primitives/data-table/use-measured-column-widths.js +83 -0
  300. package/dist/web/primitives/data-table/use-row-keyboard-navigation.d.ts +22 -0
  301. package/dist/web/primitives/data-table/use-row-keyboard-navigation.js +65 -0
  302. package/dist/web/primitives/data-table/use-row-selection.d.ts +14 -0
  303. package/dist/web/primitives/data-table/use-row-selection.js +48 -0
  304. package/dist/web/primitives/data-table/use-view-columns.d.ts +6 -0
  305. package/dist/web/primitives/data-table/use-view-columns.js +17 -0
  306. package/dist/web/primitives/data-table/view-menu.d.ts +8 -0
  307. package/dist/web/primitives/data-table/view-menu.js +45 -0
  308. package/dist/web/primitives/data-table-cell.d.ts +0 -22
  309. package/dist/web/primitives/data-table-cell.js +1 -58
  310. package/dist/web/primitives/data-table.d.ts +3 -4
  311. package/dist/web/primitives/data-table.js +30 -272
  312. package/dist/web/primitives/file-value-list.d.ts +1 -1
  313. package/dist/web/primitives/file-value-list.js +5 -5
  314. package/dist/web/primitives/form-footer-parts.d.ts +25 -0
  315. package/dist/web/primitives/form-footer-parts.js +55 -0
  316. package/dist/web/primitives/form.js +33 -43
  317. package/dist/web/primitives/input-choice-kinds.d.ts +4 -0
  318. package/dist/web/primitives/input-choice-kinds.js +67 -0
  319. package/dist/web/primitives/input-file-kinds.d.ts +3 -0
  320. package/dist/web/primitives/input-file-kinds.js +6 -0
  321. package/dist/web/primitives/input-number-kinds.d.ts +4 -0
  322. package/dist/web/primitives/input-number-kinds.js +16 -0
  323. package/dist/web/primitives/input-optional-props.d.ts +20 -0
  324. package/dist/web/primitives/input-optional-props.js +24 -0
  325. package/dist/web/primitives/input-select-presentation.d.ts +13 -0
  326. package/dist/web/primitives/input-select-presentation.js +19 -0
  327. package/dist/web/primitives/input-shared.d.ts +22 -0
  328. package/dist/web/primitives/input-shared.js +10 -0
  329. package/dist/web/primitives/input-temporal-kinds.d.ts +5 -0
  330. package/dist/web/primitives/input-temporal-kinds.js +10 -0
  331. package/dist/web/primitives/input-text-kinds.d.ts +7 -0
  332. package/dist/web/primitives/input-text-kinds.js +33 -0
  333. package/dist/web/primitives/input.js +35 -161
  334. package/dist/web/primitives/number-range-facet.d.ts +7 -0
  335. package/dist/web/primitives/number-range-facet.js +33 -0
  336. package/dist/web/widgets/bounded-load-cache.d.ts +5 -1
  337. package/dist/web/widgets/bounded-load-cache.js +48 -26
  338. package/dist/web/widgets/chart-data.d.ts +35 -0
  339. package/dist/web/widgets/chart-data.js +37 -0
  340. package/dist/web/widgets/chart-markers.d.ts +26 -0
  341. package/dist/web/widgets/chart-markers.js +64 -0
  342. package/dist/web/widgets/chart-parts.d.ts +20 -0
  343. package/dist/web/widgets/chart-parts.js +15 -0
  344. package/dist/web/widgets/charts.d.ts +5 -78
  345. package/dist/web/widgets/charts.js +7 -485
  346. package/dist/web/widgets/drawer-panel-class.d.ts +3 -0
  347. package/dist/web/widgets/drawer-panel-class.js +40 -0
  348. package/dist/web/widgets/drawer-resize-controls.d.ts +11 -0
  349. package/dist/web/widgets/drawer-resize-controls.js +12 -0
  350. package/dist/web/widgets/drawer-sizing.d.ts +19 -0
  351. package/dist/web/widgets/drawer-sizing.js +60 -0
  352. package/dist/web/widgets/drawer.js +17 -116
  353. package/dist/web/widgets/pdf-preview.js +9 -4
  354. package/dist/web/widgets/stacked-area-brush.d.ts +13 -0
  355. package/dist/web/widgets/stacked-area-brush.js +91 -0
  356. package/dist/web/widgets/stacked-area-chart.d.ts +6 -0
  357. package/dist/web/widgets/stacked-area-chart.js +45 -0
  358. package/dist/web/widgets/stacked-area-chrome.d.ts +7 -0
  359. package/dist/web/widgets/stacked-area-chrome.js +77 -0
  360. package/dist/web/widgets/stacked-area-geometry.d.ts +54 -0
  361. package/dist/web/widgets/stacked-area-geometry.js +83 -0
  362. package/dist/web/widgets/stacked-area-model.d.ts +11 -0
  363. package/dist/web/widgets/stacked-area-model.js +1 -0
  364. package/dist/web/widgets/stacked-area-plot.d.ts +3 -0
  365. package/dist/web/widgets/stacked-area-plot.js +51 -0
  366. package/dist/web/widgets/stacked-area-props.d.ts +83 -0
  367. package/dist/web/widgets/stacked-area-props.js +16 -0
  368. package/dist/web/widgets/stacked-area-svg.d.ts +3 -0
  369. package/dist/web/widgets/stacked-area-svg.js +38 -0
  370. package/dist/web/widgets/stacked-area-tooltip.d.ts +3 -0
  371. package/dist/web/widgets/stacked-area-tooltip.js +86 -0
  372. package/dist/web/widgets/use-drawer-resize.d.ts +22 -0
  373. package/dist/web/widgets/use-drawer-resize.js +52 -0
  374. package/dist/web/widgets/use-stacked-area-tooltip.d.ts +9 -0
  375. package/dist/web/widgets/use-stacked-area-tooltip.js +34 -0
  376. package/dist/web/widgets/use-stacked-area-window.d.ts +20 -0
  377. package/dist/web/widgets/use-stacked-area-window.js +76 -0
  378. package/package.json +9 -9
  379. package/src/changes.json +163 -0
@@ -0,0 +1,14 @@
1
+ import type { DataTableViewOptions } from "../../../data-table-view-types.ts";
2
+ type DensityClasses = {
3
+ /** Table body row. */
4
+ readonly row: string;
5
+ /** Table body cell; `!` beats the fill-height table's own cell padding. */
6
+ readonly cell: string;
7
+ /** Header cell; keeps the horizontal inset in line with the body cells. */
8
+ readonly header: string;
9
+ /** Card row below the md breakpoint. */
10
+ readonly card: string;
11
+ };
12
+ /** Comfortable (and a table without `view`) adds no class at all, so the markup stays as it was. */
13
+ export declare function densityClasses(view: DataTableViewOptions | undefined, keepsFillEdgeInset: boolean): DensityClasses;
14
+ export {};
@@ -0,0 +1,20 @@
1
+ const NO_DENSITY_CLASSES = { row: "", cell: "", header: "", card: "" };
2
+ // A fill-height table insets its first and last column by 24px; compact keeps that edge.
3
+ const FILL_EDGE_INSET = " first:pl-6! last:pr-6!";
4
+ const DENSITY_CLASSES = {
5
+ comfortable: NO_DENSITY_CLASSES,
6
+ compact: { row: "h-8", cell: "p-1!", header: "px-1!", card: "min-h-14 py-2" },
7
+ };
8
+ /** Comfortable (and a table without `view`) adds no class at all, so the markup stays as it was. */
9
+ export function densityClasses(view, keepsFillEdgeInset) {
10
+ if (view === undefined)
11
+ return NO_DENSITY_CLASSES;
12
+ const classes = DENSITY_CLASSES[view.density];
13
+ if (!keepsFillEdgeInset || classes.cell === "")
14
+ return classes;
15
+ return {
16
+ ...classes,
17
+ cell: classes.cell + FILL_EDGE_INSET,
18
+ header: classes.header + FILL_EDGE_INSET,
19
+ };
20
+ }
@@ -0,0 +1,5 @@
1
+ import { type ReactNode } from "react";
2
+ import type { DataTableExport } from "../../../data-table-view-types.ts";
3
+ export declare function ExportButton({ source }: {
4
+ readonly source: DataTableExport;
5
+ }): ReactNode;
@@ -0,0 +1,37 @@
1
+ import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Download } from "lucide-react";
3
+ import { useState } from "react";
4
+ import { useOptionalTranslation } from "../../../index.js";
5
+ import { requestXlsxExport } from "../../lib/export-xlsx-request.js";
6
+ import { saveBlobAsFile } from "../../lib/file-ref-download.js";
7
+ import { Button as UiButton } from "../../ui/button.js";
8
+ function localDateStamp(now) {
9
+ const pad = (n) => String(n).padStart(2, "0");
10
+ return `${now.getFullYear()}-${pad(now.getMonth() + 1)}-${pad(now.getDate())}`;
11
+ }
12
+ export function ExportButton({ source }) {
13
+ const translate = useOptionalTranslation();
14
+ const [busy, setBusy] = useState(false);
15
+ const [outcome, setOutcome] = useState(undefined);
16
+ const run = async () => {
17
+ setBusy(true);
18
+ setOutcome(undefined);
19
+ const result = await requestXlsxExport(source);
20
+ setBusy(false);
21
+ if (result.status === "ok") {
22
+ saveBlobAsFile(`${source.screenShortId}-${localDateStamp(new Date())}.xlsx`, result.blob);
23
+ }
24
+ else {
25
+ setOutcome(result.status === "too-many" ? { kind: "too-many", maxRows: result.maxRows } : "failed");
26
+ }
27
+ };
28
+ const hint = outcome === undefined
29
+ ? undefined
30
+ : outcome === "failed"
31
+ ? (translate?.("kumiko.list.export-failed") ?? "The export failed. Try again.")
32
+ : (translate?.("kumiko.list.export-too-many", { max: outcome.maxRows ?? "" }) ??
33
+ "Too many rows. Narrow the list with a filter and export again.");
34
+ return (_jsxs(_Fragment, { children: [_jsxs(UiButton, { type: "button", variant: "outline", size: "sm", className: "h-9", disabled: busy, onClick: () => void run(), "data-testid": "list-export", children: [_jsx(Download, { "aria-hidden": "true" }), busy
35
+ ? (translate?.("kumiko.list.export-busy") ?? "Exporting…")
36
+ : (translate?.("kumiko.list.export") ?? "Export")] }), hint !== undefined && (_jsx("span", { role: outcome === "failed" ? "alert" : "status", className: "text-sm text-muted-foreground", "data-testid": "list-export-hint", children: hint }))] }));
37
+ }
@@ -0,0 +1,8 @@
1
+ import type { ReactNode } from "react";
2
+ import { type DataTableProps } from "../../../index.ts";
3
+ type FacetClusterProps = Pick<DataTableProps, "filterFacets" | "filterValues" | "onFilterChange" | "onFilterReset" | "dateRangeFacets" | "onDateRangeChange" | "rangeFacets" | "onRangeChange" | "resetAll" | "notice"> & {
4
+ readonly dense: boolean;
5
+ };
6
+ /** The toolbar's filter controls; undefined when the list has none. */
7
+ export declare function buildFacetCluster({ filterFacets, filterValues, onFilterChange, onFilterReset, dateRangeFacets, onDateRangeChange, rangeFacets, onRangeChange, resetAll, notice, dense, }: FacetClusterProps): ReactNode | undefined;
8
+ export {};
@@ -0,0 +1,37 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { X } from "lucide-react";
3
+ import { useOptionalTranslation } from "../../../index.js";
4
+ import { Button as UiButton } from "../../ui/button.js";
5
+ import { DateRangeFacetFilter, FacetFilter } from "../facet-filter.js";
6
+ import { NumberRangeFacetFilter } from "../number-range-facet.js";
7
+ import { ResetAllButton } from "./reset-all-button.js";
8
+ function ListNotice({ notice }) {
9
+ const translate = useOptionalTranslation();
10
+ const fallback = notice === "truncated"
11
+ ? "Not every group is shown. Please narrow the list with a filter."
12
+ : "The search matched too many entries; groups and totals cover only part of them. Please filter further.";
13
+ return (_jsx("p", { role: "status", "data-testid": "list-notice", "data-notice": notice, className: "m-0 basis-full text-xs text-muted-foreground", children: translate?.(`kumiko.list.notice.${notice}`) ?? fallback }));
14
+ }
15
+ function FacetResetButton({ onReset }) {
16
+ const translate = useOptionalTranslation();
17
+ return (_jsxs(UiButton, { variant: "ghost", size: "sm", className: "h-9 px-2", onClick: onReset, "data-testid": "facet-reset", children: [translate?.("kumiko.list.filter.reset") ?? "Reset", _jsx(X, {})] }));
18
+ }
19
+ /** The toolbar's filter controls; undefined when the list has none. */
20
+ export function buildFacetCluster({ filterFacets, filterValues, onFilterChange, onFilterReset, dateRangeFacets, onDateRangeChange, rangeFacets, onRangeChange, resetAll, notice, dense, }) {
21
+ const hasFacets = filterFacets !== undefined && filterFacets.length > 0 && onFilterChange !== undefined;
22
+ const hasDateRangeFacets = dateRangeFacets !== undefined && dateRangeFacets.length > 0 && onDateRangeChange !== undefined;
23
+ const hasRangeFacets = rangeFacets !== undefined && rangeFacets.length > 0 && onRangeChange !== undefined;
24
+ const hasActiveFilters = filterValues !== undefined && Object.values(filterValues).some((v) => v.length > 0);
25
+ const showsResetAll = resetAll?.active === true;
26
+ if (!hasFacets &&
27
+ !hasDateRangeFacets &&
28
+ !hasRangeFacets &&
29
+ !showsResetAll &&
30
+ notice === undefined) {
31
+ return undefined;
32
+ }
33
+ return (_jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [hasFacets &&
34
+ filterFacets.map((facet) => (_jsx(FacetFilter, { facet: facet, selected: filterValues?.[facet.field] ?? [], onChange: onFilterChange, dense: dense }, facet.field))), hasDateRangeFacets &&
35
+ dateRangeFacets.map((facet) => (_jsx(DateRangeFacetFilter, { facet: facet, onChange: onDateRangeChange }, facet.field))), hasRangeFacets &&
36
+ rangeFacets.map((facet) => (_jsx(NumberRangeFacetFilter, { facet: facet, onChange: onRangeChange }, facet.field))), resetAll === undefined && hasActiveFilters && onFilterReset !== undefined && (_jsx(FacetResetButton, { onReset: onFilterReset })), showsResetAll && _jsx(ResetAllButton, { onReset: resetAll.onReset }), notice !== undefined && _jsx(ListNotice, { notice: notice })] }));
37
+ }
@@ -0,0 +1,28 @@
1
+ import type { ReactNode } from "react";
2
+ import type { DataTableServerGroup, DataTableServerGroups } from "../../../data-table-aggregate-types.ts";
3
+ import type { ListColumnViewModel, ListRowViewModel } from "../../../headless/index.ts";
4
+ type ServerGroupItem = {
5
+ readonly group: DataTableServerGroup;
6
+ readonly owner: DataTableServerGroups;
7
+ };
8
+ export type DataTableItem = {
9
+ readonly kind: "row";
10
+ readonly row: ListRowViewModel;
11
+ } | ({
12
+ readonly kind: "serverGroup";
13
+ } & ServerGroupItem) | ({
14
+ readonly kind: "groupMore";
15
+ } & ServerGroupItem);
16
+ export type DataTableBandItem = Exclude<DataTableItem, {
17
+ readonly kind: "row";
18
+ }>;
19
+ /** Server groups slice the flat `rows` by each group's `rowCount`, in group order. */
20
+ export declare function buildDataTableItems(rows: readonly ListRowViewModel[], serverGroups: DataTableServerGroups | undefined): readonly DataTableItem[];
21
+ export declare function bandReactKey(item: DataTableBandItem): string;
22
+ export declare function bandTestId(item: DataTableBandItem): string;
23
+ export declare function bandClassName(item: DataTableBandItem): string;
24
+ export declare function GroupBand({ item, columnOf, }: {
25
+ readonly item: DataTableBandItem;
26
+ readonly columnOf: (field: string) => ListColumnViewModel | undefined;
27
+ }): ReactNode;
28
+ export {};
@@ -0,0 +1,53 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useOptionalTranslation } from "../../../index.js";
3
+ import { Button as UiButton } from "../../ui/button.js";
4
+ import { ServerGroupHeader } from "./server-group-header.js";
5
+ /** Server groups slice the flat `rows` by each group's `rowCount`, in group order. */
6
+ export function buildDataTableItems(rows, serverGroups) {
7
+ if (serverGroups === undefined)
8
+ return rows.map((row) => ({ kind: "row", row }));
9
+ const items = [];
10
+ let offset = 0;
11
+ for (const group of serverGroups.groups) {
12
+ items.push({ kind: "serverGroup", group, owner: serverGroups });
13
+ const members = rows.slice(offset, offset + group.rowCount);
14
+ offset += group.rowCount;
15
+ if (group.collapsed)
16
+ continue;
17
+ for (const row of members)
18
+ items.push({ kind: "row", row });
19
+ if (group.hasMore || group.loading)
20
+ items.push({ kind: "groupMore", group, owner: serverGroups });
21
+ }
22
+ return items;
23
+ }
24
+ function bandGroupKey(item) {
25
+ return item.group.key;
26
+ }
27
+ export function bandReactKey(item) {
28
+ return `group-${bandGroupKey(item)}${item.kind === "groupMore" ? "-more" : ""}`;
29
+ }
30
+ export function bandTestId(item) {
31
+ return `row-group-${bandGroupKey(item)}${item.kind === "groupMore" ? "-more" : ""}`;
32
+ }
33
+ export function bandClassName(item) {
34
+ return item.kind === "groupMore" ? "p-0" : "bg-muted/40 p-0";
35
+ }
36
+ function GroupLoadMore({ item }) {
37
+ const translate = useOptionalTranslation();
38
+ const { group, owner } = item;
39
+ return (_jsxs("div", { className: "flex items-center gap-3 px-4 py-1.5 text-xs text-muted-foreground", children: [_jsx(UiButton, { type: "button", variant: "ghost", size: "sm", disabled: group.loading, onClick: () => owner.onLoadMore(group.key), "data-testid": `row-group-${group.key}-load-more`, children: group.loading
40
+ ? (translate?.("kumiko.list.group.loading") ?? "Loading…")
41
+ : (translate?.("kumiko.list.group.load-more") ?? "Load more") }), _jsx("span", { className: "tabular-nums", children: translate?.("kumiko.list.group.loaded", {
42
+ loaded: group.rowCount.toLocaleString(),
43
+ count: group.count.toLocaleString(),
44
+ }) ?? `${group.rowCount} / ${group.count}` })] }));
45
+ }
46
+ export function GroupBand({ item, columnOf, }) {
47
+ switch (item.kind) {
48
+ case "serverGroup":
49
+ return (_jsx(ServerGroupHeader, { group: item.group, groupColumn: columnOf(item.owner.field), columnOf: columnOf, onToggle: item.owner.onToggle, onVisible: item.owner.onHeaderVisible }));
50
+ case "groupMore":
51
+ return _jsx(GroupLoadMore, { item: item });
52
+ }
53
+ }
@@ -0,0 +1,10 @@
1
+ import type { ReactNode } from "react";
2
+ import type { DataTableMeasure } from "../../../data-table-aggregate-types.ts";
3
+ import type { ListColumnViewModel } from "../../../headless/index.ts";
4
+ export declare function measureLabel(measure: DataTableMeasure, column: ListColumnViewModel | undefined, translate: ((key: string, params?: Readonly<Record<string, unknown>>) => string) | undefined): string;
5
+ export declare function measureKindLabel(kind: DataTableMeasure["kind"], translate: ((key: string, params?: Readonly<Record<string, unknown>>) => string) | undefined): string;
6
+ /** One measure formatted like its column; a money measure lists every currency. */
7
+ export declare function MeasureValue({ measure, column, }: {
8
+ readonly measure: DataTableMeasure;
9
+ readonly column: ListColumnViewModel | undefined;
10
+ }): ReactNode;
@@ -0,0 +1,28 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { isListAggregateCurrencyAmounts } from "@cosmicdrift/kumiko-types/list-aggregate-types";
3
+ import { useOptionalLocale, useOptionalTranslation } from "../../../index.js";
4
+ import { DataTableCell } from "../data-table-cell.js";
5
+ // Money measures arrive in the storage scale (minor units, ×100), one entry per currency.
6
+ const MONEY_STORAGE_SCALE = 100;
7
+ const NO_ROW = {};
8
+ function cellTypeOf(measure, column) {
9
+ return measure.kind === "count" ? "number" : (column?.type ?? "number");
10
+ }
11
+ export function measureLabel(measure, column, translate) {
12
+ const label = column?.label ?? measure.field;
13
+ return (translate?.(`kumiko.list.measure.${measure.kind}`, { label }) ?? `${measure.kind} ${label}`);
14
+ }
15
+ export function measureKindLabel(kind, translate) {
16
+ return translate?.(`kumiko.list.measure.kind.${kind}`) ?? kind;
17
+ }
18
+ /** One measure formatted like its column; a money measure lists every currency. */
19
+ export function MeasureValue({ measure, column, }) {
20
+ const translate = useOptionalTranslation();
21
+ const locale = useOptionalLocale();
22
+ const type = cellTypeOf(measure, column);
23
+ const cell = (value, key) => (_jsx(DataTableCell, { value: value, row: NO_ROW, field: measure.field, type: type, renderer: measure.kind === "count" ? undefined : column?.renderer, translate: translate, locale: locale, ...(column?.optionLabels !== undefined && { optionLabels: column.optionLabels }), ...(column?.grouping !== undefined && { grouping: column.grouping }) }, key));
24
+ const { value } = measure;
25
+ if (!isListAggregateCurrencyAmounts(value))
26
+ return cell(value, measure.kind);
27
+ return (_jsx("span", { className: "inline-flex flex-col items-end", children: value.map((entry) => cell({ amount: entry.value / MONEY_STORAGE_SCALE, currency: entry.currency }, entry.currency)) }));
28
+ }
@@ -0,0 +1,8 @@
1
+ import type { ReactNode } from "react";
2
+ import type { DataTableColumnPins } from "../../../data-table-view-types.ts";
3
+ export declare function PinSection({ fields, labels, pins, onChange, }: {
4
+ readonly fields: readonly string[];
5
+ readonly labels: ReadonlyMap<string, string>;
6
+ readonly pins: DataTableColumnPins;
7
+ readonly onChange: (next: DataTableColumnPins) => void;
8
+ }): ReactNode;
@@ -0,0 +1,20 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { PanelLeft, PanelRight } from "lucide-react";
3
+ import { useOptionalTranslation } from "../../../index.js";
4
+ import { Button as UiButton } from "../../ui/button.js";
5
+ function togglePin(pins, field, side) {
6
+ const { [field]: current, ...others } = pins;
7
+ return current === side ? others : { ...others, [field]: side };
8
+ }
9
+ function PinButton({ field, label, side, pinned, onClick, }) {
10
+ const translate = useOptionalTranslation();
11
+ const Icon = side === "left" ? PanelLeft : PanelRight;
12
+ const ariaLabel = translate?.(`kumiko.list.view.pin-${side}`, { column: label }) ?? `Pin ${label} ${side}`;
13
+ return (_jsx(UiButton, { type: "button", variant: "ghost", size: "icon", className: "size-6 aria-pressed:bg-primary aria-pressed:text-primary-foreground aria-pressed:hover:bg-primary/90", "aria-label": ariaLabel, "aria-pressed": pinned, "data-testid": `list-view-pin-${field}-${side}`, onClick: onClick, children: _jsx(Icon, { className: "size-3.5", "aria-hidden": "true" }) }));
14
+ }
15
+ export function PinSection({ fields, labels, pins, onChange, }) {
16
+ return (_jsx("ul", { className: "m-0 flex list-none flex-col p-0", "data-testid": "list-view-pins", children: fields.map((field) => {
17
+ const label = labels.get(field) ?? field;
18
+ return (_jsxs("li", { className: "flex h-8 items-center gap-1.5", children: [_jsx("span", { className: "min-w-0 flex-1 truncate text-sm", children: label }), ["left", "right"].map((side) => (_jsx(PinButton, { field: field, label: label, side: side, pinned: pins[field] === side, onClick: () => onChange(togglePin(pins, field, side)) }, side)))] }, field));
19
+ }) }));
20
+ }
@@ -0,0 +1,4 @@
1
+ import type { ReactNode } from "react";
2
+ export declare function ResetAllButton({ onReset }: {
3
+ readonly onReset: () => void;
4
+ }): ReactNode;
@@ -0,0 +1,8 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { X } from "lucide-react";
3
+ import { useOptionalTranslation } from "../../../index.js";
4
+ import { Button as UiButton } from "../../ui/button.js";
5
+ export function ResetAllButton({ onReset }) {
6
+ const translate = useOptionalTranslation();
7
+ return (_jsxs(UiButton, { variant: "ghost", size: "sm", className: "h-9 px-2", onClick: onReset, "data-testid": "list-reset-all", children: [translate?.("kumiko.list.reset-all") ?? "Reset", _jsx(X, {})] }));
8
+ }
@@ -0,0 +1,8 @@
1
+ import type { ReactNode } from "react";
2
+ import type { ListRowViewModel } from "../../../headless/index.ts";
3
+ import { type DataTableBandItem } from "./group-band.tsx";
4
+ import type { DataTableModel } from "./use-data-table-state.ts";
5
+ export declare function renderExpandToggle(m: DataTableModel, row: ListRowViewModel): ReactNode;
6
+ export declare function renderExpansion(m: DataTableModel, row: ListRowViewModel, as: "row" | "item", colSpan: number): ReactNode;
7
+ export declare function renderBand(m: DataTableModel, item: DataTableBandItem): ReactNode;
8
+ export declare function renderEmptyBlock(m: DataTableModel): ReactNode;
@@ -0,0 +1,35 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { cn } from "../../lib/cn.js";
3
+ import { TableCell, TableRow } from "../../ui/table.js";
4
+ import { cellTitle } from "../data-table-cell.js";
5
+ import { GroupBand } from "./group-band.js";
6
+ import { EXPANSION_CELL_CLASS, EXPANSION_CONTENT_CLASS, RowExpandToggle } from "./table-chrome.js";
7
+ // The expand toggle of a row, shared by table and card layout. Named after
8
+ // the first data column, the one a user recognizes the row by.
9
+ export function renderExpandToggle(m, row) {
10
+ const { expansion, expandedRowIds, columns, tableTranslate } = m;
11
+ if (expansion === undefined)
12
+ return null;
13
+ const expanded = expandedRowIds?.has(row.id) === true;
14
+ const firstColumn = columns[0];
15
+ const title = (firstColumn !== undefined ? cellTitle(row.values[firstColumn.field]) : undefined) ?? "";
16
+ const labelKey = expanded ? "kumiko.list.row.collapse" : "kumiko.list.row.expand";
17
+ return (_jsx(RowExpandToggle, { expanded: expanded, controlsId: m.expansionDomId(row), label: tableTranslate?.(labelKey, { title }) ?? `${expanded ? "Collapse" : "Expand"} ${title}`, onToggle: () => expansion.onToggleRowExpanded(row.id), testId: `${m.rowTestId(row)}-toggle` }));
18
+ }
19
+ export function renderExpansion(m, row, as, colSpan) {
20
+ const { expansion, expandedRowIds } = m;
21
+ if (expansion === undefined || expandedRowIds?.has(row.id) !== true)
22
+ return null;
23
+ const content = expansion.renderExpandedRow(row);
24
+ if (as === "item") {
25
+ return (_jsx("li", { id: m.expansionDomId(row), "data-testid": `${m.rowTestId(row)}-expansion`, className: "border-b border-border-row bg-muted px-4 pt-1 pb-3.5", children: content }));
26
+ }
27
+ return (_jsx(TableRow, { id: m.expansionDomId(row), "data-testid": `${m.rowTestId(row)}-expansion`, className: "hover:bg-transparent", children: _jsx(TableCell, { colSpan: colSpan, className: EXPANSION_CELL_CLASS, children: _jsx("div", { className: EXPANSION_CONTENT_CLASS, children: content }) }) }));
28
+ }
29
+ export function renderBand(m, item) {
30
+ return _jsx(GroupBand, { item: item, columnOf: m.columnOf });
31
+ }
32
+ export function renderEmptyBlock(m) {
33
+ const { testId, chromeless, fillsHeight, emptyState, tableTranslate } = m;
34
+ return (_jsx("div", { "data-testid": testId !== undefined ? `${testId}-empty` : "render-list-empty", className: cn("flex flex-col items-center justify-center p-12 text-sm text-muted-foreground gap-3", chromeless !== true && !fillsHeight && "rounded-md border border-dashed"), children: emptyState ?? _jsx("span", { children: tableTranslate?.("kumiko.list.no-entries") ?? "No entries." }) }));
35
+ }
@@ -0,0 +1,11 @@
1
+ import { type ReactNode } from "react";
2
+ import type { DataTableServerGroup } from "../../../data-table-aggregate-types.ts";
3
+ import type { ListColumnViewModel } from "../../../headless/index.ts";
4
+ /** Header of a server group: toggle, value, server row count and the group's measures. */
5
+ export declare function ServerGroupHeader({ group, groupColumn, columnOf, onToggle, onVisible, }: {
6
+ readonly group: DataTableServerGroup;
7
+ readonly groupColumn: ListColumnViewModel | undefined;
8
+ readonly columnOf: (field: string) => ListColumnViewModel | undefined;
9
+ readonly onToggle: (key: string) => void;
10
+ readonly onVisible: (key: string) => void;
11
+ }): ReactNode;
@@ -0,0 +1,51 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { ChevronDown, ChevronRight } from "lucide-react";
3
+ import { useEffect, useRef } from "react";
4
+ import { useOptionalLocale, useOptionalTranslation } from "../../../index.js";
5
+ import { DataTableCell } from "../data-table-cell.js";
6
+ import { MeasureValue, measureLabel } from "./measure-value.js";
7
+ const NO_ROW = {};
8
+ // Reports the header once it scrolls near the viewport; without an observer
9
+ // (tests, old engines) every header counts as visible right away.
10
+ function useReportWhenVisible(onVisible) {
11
+ const ref = useRef(null);
12
+ const onVisibleRef = useRef(onVisible);
13
+ onVisibleRef.current = onVisible;
14
+ useEffect(() => {
15
+ const node = ref.current;
16
+ if (node === null)
17
+ return;
18
+ if (typeof IntersectionObserver === "undefined") {
19
+ onVisibleRef.current();
20
+ return;
21
+ }
22
+ const observer = new IntersectionObserver((entries) => {
23
+ if (!entries.some((entry) => entry.isIntersecting))
24
+ return;
25
+ observer.disconnect();
26
+ onVisibleRef.current();
27
+ }, { rootMargin: "200px" });
28
+ observer.observe(node);
29
+ return () => observer.disconnect();
30
+ }, []);
31
+ return ref;
32
+ }
33
+ function GroupValueLabel({ group, column, }) {
34
+ const translate = useOptionalTranslation();
35
+ const locale = useOptionalLocale();
36
+ if (group.label !== undefined)
37
+ return group.label;
38
+ if (group.value === null || group.value === "") {
39
+ return translate?.("kumiko.list.group.empty") ?? "(empty)";
40
+ }
41
+ return (_jsx(DataTableCell, { value: group.value, row: NO_ROW, field: column?.field ?? "", type: column?.type ?? "text", translate: translate, locale: locale, ...(column?.optionLabels !== undefined && { optionLabels: column.optionLabels }), ...(column?.optionTones !== undefined && { optionTones: column.optionTones }) }));
42
+ }
43
+ /** Header of a server group: toggle, value, server row count and the group's measures. */
44
+ export function ServerGroupHeader({ group, groupColumn, columnOf, onToggle, onVisible, }) {
45
+ const translate = useOptionalTranslation();
46
+ const reportRef = useReportWhenVisible(() => onVisible(group.key));
47
+ return (_jsxs("div", { ref: reportRef, className: "flex flex-wrap items-center gap-x-4 gap-y-1 pe-4 text-xs text-foreground-secondary", children: [_jsxs("button", { type: "button", "aria-expanded": !group.collapsed, "data-testid": `row-group-${group.key}-toggle`, onClick: () => onToggle(group.key), className: "flex items-center gap-2 py-2 ps-4 text-left font-medium", children: [group.collapsed ? (_jsx(ChevronRight, { className: "size-3.5", "aria-hidden": "true" })) : (_jsx(ChevronDown, { className: "size-3.5", "aria-hidden": "true" })), _jsx(GroupValueLabel, { group: group, column: groupColumn }), _jsx("span", { "data-testid": `row-group-${group.key}-count`, className: "rounded-full bg-muted px-2 py-0.5 tabular-nums text-muted-foreground", children: group.count.toLocaleString() })] }), group.measures.map((measure) => {
48
+ const column = columnOf(measure.field);
49
+ return (_jsxs("span", { "data-testid": `row-group-${group.key}-${measure.kind}-${measure.field}`, className: "inline-flex items-center gap-1.5 tabular-nums", children: [_jsx("span", { className: "text-muted-foreground", children: measureLabel(measure, column, translate) }), _jsx(MeasureValue, { measure: measure, column: column })] }, `${measure.kind}-${measure.field}`));
50
+ })] }));
51
+ }
@@ -0,0 +1,17 @@
1
+ import type { CSSProperties, ReactNode } from "react";
2
+ import type { DataTableSort } from "../../../index.ts";
3
+ export declare function SortableHeader({ field, label, sortable, highlighted, numeric, fitContent, className, sort, onSortChange, style, columnField, trailing, }: {
4
+ readonly field: string;
5
+ readonly label: string;
6
+ readonly sortable: boolean;
7
+ readonly highlighted?: boolean;
8
+ readonly numeric?: boolean;
9
+ readonly fitContent?: boolean;
10
+ readonly className?: string;
11
+ readonly sort?: DataTableSort;
12
+ readonly onSortChange?: (next: DataTableSort | null) => void;
13
+ readonly style?: CSSProperties;
14
+ readonly columnField?: string;
15
+ /** Rendered inside the th after the label, e.g. a resize handle. */
16
+ readonly trailing?: ReactNode;
17
+ }): ReactNode;
@@ -0,0 +1,41 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { ArrowDown, ArrowUp, ArrowUpDown } from "lucide-react";
3
+ import { cn } from "../../lib/cn.js";
4
+ import { TableHead } from "../../ui/table.js";
5
+ import { FIT_CONTENT_CELL_CLASS } from "../data-table-cell.js";
6
+ // SortableHeader: renders the th header per column, with or without
7
+ // click-sort. Three paths:
8
+ // (a) sortable=false OR no onSortChange -> plain label, no cursor
9
+ // interaction (DataTable purely as a view without sort wiring).
10
+ // (b) sortable=true + onSortChange -> header is a button, click
11
+ // cycles asc -> desc -> null. aria-sort mirrors the state.
12
+ // (c) sortable=true in the schema but no onSortChange prop -> still
13
+ // label-only, but data-sortable=true stays so tests that know the
14
+ // schema view keep working.
15
+ export function SortableHeader({ field, label, sortable, highlighted, numeric, fitContent, className, sort, onSortChange, style, columnField, trailing, }) {
16
+ const active = sort?.field === field ? sort : undefined;
17
+ const ariaSort = active?.dir === "asc" ? "ascending" : active?.dir === "desc" ? "descending" : "none";
18
+ const cellExtras = {
19
+ ...(style !== undefined && { style }),
20
+ ...(columnField !== undefined && { "data-column-field": columnField }),
21
+ };
22
+ if (!sortable || onSortChange === undefined) {
23
+ return (_jsxs(TableHead, { "data-testid": `column-${field}`, "data-sortable": sortable === true ? true : undefined, "data-highlighted": highlighted === true ? "true" : undefined, className: cn("font-medium whitespace-normal text-muted-foreground", numeric === true && "text-right", fitContent === true && FIT_CONTENT_CELL_CLASS, highlighted === true && "bg-accent/40", trailing !== undefined && "group/header relative", className), ...cellExtras, children: [label, trailing] }));
24
+ }
25
+ const Icon = active?.dir === "asc" ? ArrowUp : active?.dir === "desc" ? ArrowDown : ArrowUpDown;
26
+ const next = nextSortState(active?.dir, field);
27
+ return (_jsxs(TableHead, { "data-testid": `column-${field}`, "data-sortable": "true", "data-highlighted": highlighted === true ? "true" : undefined, "aria-sort": ariaSort, className: cn(active !== undefined
28
+ ? "font-semibold text-foreground"
29
+ : "font-medium text-muted-foreground", "whitespace-normal", numeric === true && "text-right", fitContent === true && FIT_CONTENT_CELL_CLASS, highlighted === true && "bg-accent/40", trailing !== undefined && "group/header relative", className), ...cellExtras, children: [_jsxs("button", { type: "button", onClick: () => onSortChange(next), className: cn("group/sort inline-flex h-8 items-center gap-1.5 rounded-sm px-2 -mx-2 text-sm [font-weight:inherit]", "hover:bg-accent hover:text-accent-foreground", "focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring", active !== undefined && "text-foreground"), children: [_jsx("span", { children: label }), _jsx(Icon, { "data-sort-icon": active === undefined ? "idle" : "active", className: cn("size-3.5", active === undefined &&
30
+ "opacity-0 group-hover/sort:opacity-40 group-focus-visible/sort:opacity-40"), "aria-hidden": "true" })] }), trailing] }));
31
+ }
32
+ // 3-state toggle: no sort -> asc -> desc -> no sort. Idiomatic for
33
+ // power-user lists where "I want the server default order back" is a
34
+ // real action (instead of toggling asc<->desc forever).
35
+ function nextSortState(current, field) {
36
+ if (current === undefined)
37
+ return { field, dir: "asc" };
38
+ if (current === "asc")
39
+ return { field, dir: "desc" };
40
+ return null;
41
+ }
@@ -0,0 +1,3 @@
1
+ import { type ReactNode } from "react";
2
+ import type { DataTableModel } from "./use-data-table-state.ts";
3
+ export declare function renderTableBody(m: DataTableModel): ReactNode;
@@ -0,0 +1,32 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Fragment } from "react";
3
+ import { cn } from "../../lib/cn.js";
4
+ import { TableBody, TableCell, TableRow } from "../../ui/table.js";
5
+ import { rowToneClass } from "../data-table-cell.js";
6
+ import { RowActionsCell } from "../row-actions.js";
7
+ import { ActionsBodyCell } from "./actions-column.js";
8
+ import { bandClassName, bandReactKey, bandTestId } from "./group-band.js";
9
+ import { renderBand, renderExpandToggle, renderExpansion } from "./row-render-parts.js";
10
+ import { EXPAND_TOGGLE_CELL_CLASS } from "./table-chrome.js";
11
+ import { renderDataCell } from "./table-data-cell.js";
12
+ import { EXPAND_COLUMN_KEY, SELECT_COLUMN_KEY } from "./use-column-pins.js";
13
+ function renderBandRow(m, item) {
14
+ return (_jsx(TableRow, { "data-testid": bandTestId(item), children: _jsx(TableCell, { colSpan: m.fullColSpan, className: bandClassName(item), children: renderBand(m, item) }) }, bandReactKey(item)));
15
+ }
16
+ function renderExpandCell(m, row) {
17
+ const expandBodyPin = m.pins.bodyCell(EXPAND_COLUMN_KEY);
18
+ return (_jsx(TableCell, { "data-testid": m.getCellTestId?.(row, "expand") ?? `cell-${row.id}-expand`, className: cn(EXPAND_TOGGLE_CELL_CLASS, expandBodyPin.className), style: expandBodyPin.style, children: renderExpandToggle(m, row) }));
19
+ }
20
+ function renderActionsCell(m, row, tableActions) {
21
+ return (_jsx(ActionsBodyCell, { testId: m.getCellTestId?.(row, "actions") ?? `cell-${row.id}-actions`, fillsHeight: m.fillsHeight, measureContent: m.sizing !== undefined, children: _jsx(RowActionsCell, { row: row, actions: tableActions, mode: m.rowActionMode, rowIsLink: m.onRowClick !== undefined }) }));
22
+ }
23
+ function renderDataRow(m, row) {
24
+ const { rowTone, density, tableActions } = m;
25
+ const openRow = m.rowOpener(row);
26
+ const opensOnClick = openRow !== undefined;
27
+ const keyboard = m.keyboardRow(row.id, openRow);
28
+ return (_jsxs(Fragment, { children: [_jsxs(TableRow, { "data-testid": m.rowTestId(row), "data-tone": rowTone?.(row), onClick: openRow, className: cn(rowToneClass(rowTone?.(row)), opensOnClick && "cursor-pointer", m.fillsHeight && "h-10 border-border-row hover:bg-muted", density.row, keyboard.className), ...keyboard.props, children: [m.selection.bodyCell(row, m.pins.bodyCell(SELECT_COLUMN_KEY)), m.expansion !== undefined && renderExpandCell(m, row), m.columns.map((col, colIndex) => renderDataCell(m, row, col, colIndex, opensOnClick)), m.hasTableActions && tableActions !== undefined && renderActionsCell(m, row, tableActions)] }), renderExpansion(m, row, "row", m.fullColSpan)] }, row.id));
29
+ }
30
+ export function renderTableBody(m) {
31
+ return (_jsx(TableBody, { children: m.tableItems.map((item) => item.kind === "row" ? renderDataRow(m, item.row) : renderBandRow(m, item)) }));
32
+ }
@@ -0,0 +1,13 @@
1
+ import type { ReactNode, TableHTMLAttributes } from "react";
2
+ export declare function FlushTable({ className, ...props }: TableHTMLAttributes<HTMLTableElement>): ReactNode;
3
+ export declare const FILL_TABLE_CLASS: string;
4
+ export declare const EXPAND_TOGGLE_CELL_CLASS = "w-10 px-1! py-0!";
5
+ export declare const EXPANSION_CELL_CLASS = "whitespace-normal bg-muted p-0! align-top md:[&_td:last-child]:bg-muted";
6
+ export declare const EXPANSION_CONTENT_CLASS = "pt-1 pr-4 pb-3.5 pl-[52px]";
7
+ export declare function RowExpandToggle({ expanded, controlsId, label, onToggle, testId, }: {
8
+ readonly expanded: boolean;
9
+ readonly controlsId: string;
10
+ readonly label: string;
11
+ readonly onToggle: () => void;
12
+ readonly testId: string;
13
+ }): ReactNode;
@@ -0,0 +1,30 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { ChevronRight } from "lucide-react";
3
+ import { cn } from "../../lib/cn.js";
4
+ import { Button as UiButton } from "../../ui/button.js";
5
+ // The vendored Table wraps <table> in an `overflow-x-auto` container, which
6
+ // would become the scrollport of a sticky header. A scrollBody table scrolls
7
+ // in its parent instead, so it renders the bare <table>.
8
+ export function FlushTable({ className, ...props }) {
9
+ return (_jsx("table", { "data-slot": "table", className: cn("w-full caption-bottom text-sm", className), ...props }));
10
+ }
11
+ // Header cells stick to the scroll surface; the bottom rule is an inset
12
+ // shadow because a border on a sticky cell scrolls away under border-collapse.
13
+ // Column weight (medium vs. semibold for the sorted one) lives on the th itself
14
+ // in SortableHeader: a `[&_th]:font-*` rule here would outrank it.
15
+ // Child selectors only: an expanded row nests a whole table inside a cell, and
16
+ // descendant selectors would make its header sticky and re-pad its cells.
17
+ export const FILL_TABLE_CLASS = cn("[&>thead>tr>th]:sticky [&>thead>tr>th]:top-0 [&>thead>tr>th]:z-10 [&>thead>tr>th]:h-9 [&>thead>tr>th]:bg-muted [&>thead>tr>th]:px-2 [&>thead>tr>th]:text-[13px]", "[&>thead>tr>th]:shadow-[inset_0_-1px_0_var(--color-border)] [&>thead>tr>th_button]:text-[13px] [&>tbody>tr>td]:px-2 [&>tbody>tr>td]:py-0", "[&>*>tr>:first-child]:pl-6 [&>*>tr>:last-child]:pr-6");
18
+ // `!`: beats the first/last-child padding of FILL_TABLE_CLASS, which would
19
+ // otherwise widen the narrow toggle column / pad the full-width expansion cell.
20
+ // The nested table's sticky actions cell (last td) gets the muted surface
21
+ // instead of its own md:bg-background, so it blends into the area.
22
+ export const EXPAND_TOGGLE_CELL_CLASS = "w-10 px-1! py-0!";
23
+ export const EXPANSION_CELL_CLASS = "whitespace-normal bg-muted p-0! align-top md:[&_td:last-child]:bg-muted";
24
+ export const EXPANSION_CONTENT_CLASS = "pt-1 pr-4 pb-3.5 pl-[52px]";
25
+ export function RowExpandToggle({ expanded, controlsId, label, onToggle, testId, }) {
26
+ return (_jsx(UiButton, { type: "button", variant: "ghost", size: "icon", className: "size-8 shrink-0 max-md:size-11", "aria-expanded": expanded, ...(expanded && { "aria-controls": controlsId }), "aria-label": label, "data-testid": testId, onClick: (e) => {
27
+ e.stopPropagation();
28
+ onToggle();
29
+ }, onKeyDown: (e) => e.stopPropagation(), children: _jsx(ChevronRight, { className: cn("size-4 transition-transform", expanded && "rotate-90"), "aria-hidden": "true" }) }));
30
+ }
@@ -0,0 +1,3 @@
1
+ import type { ReactNode } from "react";
2
+ import type { DataTableModel } from "./use-data-table-state.ts";
3
+ export declare function renderTableContent(m: DataTableModel): ReactNode;
@@ -0,0 +1,41 @@
1
+ import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { InfiniteSentinel, Pager } from "../pager.js";
3
+ import { renderCardsView } from "./card-view.js";
4
+ import { renderEmptyBlock } from "./row-render-parts.js";
5
+ import { DataTableFooter } from "./table-footer.js";
6
+ import { renderTableView } from "./table-view.js";
7
+ // The pager is ALWAYS rendered below the table (even with rows=[]) so the
8
+ // user can page back on a filter hit of zero without losing the list.
9
+ // Except total === 0: then there is nothing to paginate. Incompatible with
10
+ // infinite scroll: the caller sets either pager OR onReachEnd.
11
+ function renderPager(m) {
12
+ const { pager, serverGroups, itemNoun, testId } = m;
13
+ if (pager === undefined || !(pager.total > 0) || serverGroups !== undefined)
14
+ return undefined;
15
+ return (_jsx(Pager, { page: pager.page, limit: pager.limit, total: pager.total, onPageChange: pager.onPageChange, ...(pager.pageSizeOptions !== undefined && { pageSizeOptions: pager.pageSizeOptions }), ...(pager.onPageSizeChange !== undefined && { onPageSizeChange: pager.onPageSizeChange }), ...(itemNoun !== undefined && { itemNoun }), testId: testId !== undefined ? `${testId}-pager` : "render-list-pager", pinned: m.fillsHeight }));
16
+ }
17
+ function renderSentinel(m, pagerElement) {
18
+ const { onReachEnd, serverGroups, testId } = m;
19
+ if (pagerElement !== undefined || onReachEnd === undefined || serverGroups !== undefined) {
20
+ return undefined;
21
+ }
22
+ return (_jsx(InfiniteSentinel, { onReachEnd: onReachEnd, loadingMore: m.loadingMore === true, hasMore: m.hasMore !== false, rowsCount: m.rows.length, testId: testId !== undefined ? `${testId}-sentinel` : "render-list-sentinel" }));
23
+ }
24
+ function renderTableBodyContent(m) {
25
+ if (m.isEmpty)
26
+ return renderEmptyBlock(m);
27
+ return m.isNarrow ? renderCardsView(m) : renderTableView(m);
28
+ }
29
+ export function renderTableContent(m) {
30
+ const { testId, itemNoun, serverGroups } = m;
31
+ const tableContent = renderTableBodyContent(m);
32
+ const pagerElement = renderPager(m);
33
+ const sentinelElement = renderSentinel(m, pagerElement);
34
+ // With scrollBody the footer (pager, else the entry count) sits OUTSIDE the
35
+ // scroll surface so it stays put at any row count; the infinite sentinel
36
+ // stays INSIDE, or it would never intersect the scrollport.
37
+ if (!m.fillsHeight) {
38
+ return (_jsxs(_Fragment, { children: [m.selection.bar, tableContent, pagerElement, sentinelElement] }));
39
+ }
40
+ return (_jsxs(_Fragment, { children: [m.selection.bar, _jsxs("div", { "data-testid": testId !== undefined ? `${testId}-scroll` : "render-list-scroll", className: "min-h-0 flex-1 overflow-auto border-t border-border bg-card", children: [tableContent, sentinelElement] }), pagerElement ?? (_jsx(DataTableFooter, { count: serverGroups?.totalCount ?? m.rows.length, ...(itemNoun !== undefined && { itemNoun }), testId: testId !== undefined ? `${testId}-footer` : "render-list-footer" }))] }));
41
+ }
@@ -0,0 +1,4 @@
1
+ import type { ReactNode } from "react";
2
+ import type { ListColumnViewModel, ListRowViewModel } from "../../../headless/index.ts";
3
+ import type { DataTableModel } from "./use-data-table-state.ts";
4
+ export declare function renderDataCell(m: DataTableModel, row: ListRowViewModel, col: ListColumnViewModel, colIndex: number, opensOnClick: boolean): ReactNode;