@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,35 @@
1
+ import type { DashboardScreenDefinition } from "@cosmicdrift/kumiko-framework/ui-types";
2
+ import type { ErrorNotice } from "../../app/error-reference.tsx";
3
+ import type { Translate } from "../../headless/index.ts";
4
+ import { type useNav } from "../../index.ts";
5
+ import { type ScreenParams } from "./dashboard-shared.tsx";
6
+ type Nav = ReturnType<typeof useNav>;
7
+ export declare function useDashboardScreenParams(screen: DashboardScreenDefinition, translate: Translate, nav: Nav): {
8
+ recordBinding: import("@cosmicdrift/kumiko-types/screen").DashboardRecordBinding | undefined;
9
+ recordId: string;
10
+ recordMissing: boolean;
11
+ recordPayloadKey: string | undefined;
12
+ recordParams: Readonly<Record<string, unknown>>;
13
+ recordQueryName: string | undefined;
14
+ picker: import("react").ReactNode;
15
+ rangeControl: import("react").ReactNode;
16
+ screenParams: ScreenParams;
17
+ loadedAtMs: number;
18
+ reportLoaded: (atMs: number) => void;
19
+ };
20
+ export declare function useDashboardRecord(screen: DashboardScreenDefinition, params: Pick<ReturnType<typeof useDashboardScreenParams>, "recordQueryName" | "recordParams" | "recordMissing">): {
21
+ recordQuery: import("../../index.ts").UseQueryResult<Readonly<Record<string, unknown>>>;
22
+ recordRow: Readonly<Record<string, unknown>> | null;
23
+ refetchRecord: () => Promise<void>;
24
+ queryRuntime: {
25
+ refreshIntervalMs: number | undefined;
26
+ register: (refetch: () => Promise<void>) => () => void;
27
+ };
28
+ refetchAll: () => Promise<void>;
29
+ };
30
+ export declare function useDashboardHeaderActions(screen: DashboardScreenDefinition, params: Pick<ReturnType<typeof useDashboardScreenParams>, "recordPayloadKey" | "recordId" | "recordQueryName" | "recordMissing">, record: Pick<ReturnType<typeof useDashboardRecord>, "recordRow" | "refetchAll">, translate: Translate): {
31
+ actionError: ErrorNotice | null;
32
+ setActionError: import("react").Dispatch<import("react").SetStateAction<ErrorNotice | null>>;
33
+ headerActions: readonly import("../../index.ts").RenderEditAction[] | undefined;
34
+ };
35
+ export {};
@@ -0,0 +1,69 @@
1
+ import { useCallback, useEffect, useMemo, useState } from "react";
2
+ import { useQuery, useRecordActions } from "../../index.js";
3
+ import { resolveRefreshIntervalMs } from "./dashboard-refresh.js";
4
+ import { useFilterParams, useTimeRange } from "./dashboard-screen-controls.js";
5
+ import { useLoadedAt, useRefetchRegistry } from "./dashboard-shared.js";
6
+ export function useDashboardScreenParams(screen, translate, nav) {
7
+ const recordBinding = screen.record;
8
+ const recordId = recordBinding !== undefined ? (nav.searchParams[recordBinding.param] ?? "") : "";
9
+ const recordMissing = recordBinding !== undefined && recordId === "";
10
+ const recordPayloadKey = recordBinding?.payloadKey ?? recordBinding?.param;
11
+ const recordParams = recordPayloadKey !== undefined && recordId !== "" ? { [recordPayloadKey]: recordId } : {};
12
+ const recordQueryName = recordBinding?.query;
13
+ const { params: filterParams, picker } = useFilterParams(screen, !recordMissing);
14
+ const { params: rangeParams, control: rangeControl } = useTimeRange(screen, translate);
15
+ const screenParams = { filterParams, rangeParams, recordParams };
16
+ const { loadedAtMs, reportLoaded } = useLoadedAt(screenParams);
17
+ return {
18
+ recordBinding,
19
+ recordId,
20
+ recordMissing,
21
+ recordPayloadKey,
22
+ recordParams,
23
+ recordQueryName,
24
+ picker,
25
+ rangeControl,
26
+ screenParams,
27
+ loadedAtMs,
28
+ reportLoaded,
29
+ };
30
+ }
31
+ export function useDashboardRecord(screen, params) {
32
+ const { recordQueryName, recordParams, recordMissing } = params;
33
+ // The record query is not a panel query: it needs the interval before the
34
+ // registry context below exists, and `refresh.field` reads from its own row.
35
+ const [refreshIntervalMs, setRefreshIntervalMs] = useState(() => resolveRefreshIntervalMs(screen.refresh, null));
36
+ const recordQuery = useQuery(recordQueryName ?? "", recordParams, {
37
+ enabled: recordQueryName !== undefined && !recordMissing,
38
+ live: true,
39
+ ...(refreshIntervalMs !== undefined && { refetchIntervalMs: refreshIntervalMs }),
40
+ });
41
+ const { data: recordRow, refetch: refetchRecord } = recordQuery;
42
+ useEffect(() => {
43
+ setRefreshIntervalMs(resolveRefreshIntervalMs(screen.refresh, recordRow));
44
+ }, [screen.refresh, recordRow]);
45
+ const { register, refetchRegistered } = useRefetchRegistry();
46
+ const queryRuntime = useMemo(() => ({ refreshIntervalMs, register }), [refreshIntervalMs, register]);
47
+ const refetchAll = useCallback(async () => {
48
+ await Promise.all([
49
+ recordQueryName !== undefined ? refetchRecord() : Promise.resolve(),
50
+ refetchRegistered(),
51
+ ]);
52
+ }, [recordQueryName, refetchRecord, refetchRegistered]);
53
+ return { recordQuery, recordRow, refetchRecord, queryRuntime, refetchAll };
54
+ }
55
+ const NO_RECORD_ROW = {};
56
+ export function useDashboardHeaderActions(screen, params, record, translate) {
57
+ const { recordPayloadKey, recordId, recordQueryName, recordMissing } = params;
58
+ const [actionError, setActionError] = useState(null);
59
+ const idOnlyRow = useMemo(() => (recordPayloadKey !== undefined ? { id: recordId, [recordPayloadKey]: recordId } : null), [recordId, recordPayloadKey]);
60
+ const actionRow = recordQueryName !== undefined ? record.recordRow : idOnlyRow;
61
+ const headerActions = useRecordActions({
62
+ actions: actionRow !== null && !recordMissing ? screen.actions : undefined,
63
+ record: actionRow ?? NO_RECORD_ROW,
64
+ recordId,
65
+ translate,
66
+ onWriteSuccess: record.refetchAll,
67
+ });
68
+ return { actionError, setActionError, headerActions };
69
+ }
@@ -13,6 +13,7 @@ export { KumikoLink, useBrowserNavApi } from "./app/nav.tsx";
13
13
  export { PlainContentEditor } from "./app/plain-content-editor.tsx";
14
14
  export { useResolvers } from "./app/resolvers-context.tsx";
15
15
  export { RichContentEditor } from "./app/rich-content-editor.tsx";
16
+ export { clearRememberedListState } from "./app/session-list-state-store.ts";
16
17
  export type { NavIconKey } from "./icons.tsx";
17
18
  export { Icon } from "./icons.tsx";
18
19
  export type { AppLayoutProps } from "./layout/app-layout.tsx";
package/dist/web/index.js CHANGED
@@ -12,6 +12,7 @@ export { KumikoLink, useBrowserNavApi } from "./app/nav.js";
12
12
  export { PlainContentEditor } from "./app/plain-content-editor.js";
13
13
  export { useResolvers } from "./app/resolvers-context.js";
14
14
  export { RichContentEditor } from "./app/rich-content-editor.js";
15
+ export { clearRememberedListState } from "./app/session-list-state-store.js";
15
16
  export { Icon } from "./icons.js";
16
17
  export { AppLayout } from "./layout/app-layout.js";
17
18
  export { Avatar } from "./layout/avatar.js";
@@ -0,0 +1,11 @@
1
+ import type { DataTableExport } from "../../data-table-view-types.ts";
2
+ export type XlsxExportResult = {
3
+ readonly status: "ok";
4
+ readonly blob: Blob;
5
+ } | {
6
+ readonly status: "too-many";
7
+ readonly maxRows: number | undefined;
8
+ } | {
9
+ readonly status: "failed";
10
+ };
11
+ export declare function requestXlsxExport(source: DataTableExport): Promise<XlsxExportResult>;
@@ -0,0 +1,46 @@
1
+ import { isPlainObject } from "@cosmicdrift/kumiko-types/type-guards";
2
+ import { CSRF_HEADER_NAME, LOCALE_HEADER_NAME, readActiveLocale, readCsrfToken, } from "../../live/index.js";
3
+ const TOO_MANY_ROWS_HTTP_STATUS = 413;
4
+ async function readMaxRows(res) {
5
+ try {
6
+ const body = await res.json();
7
+ const error = isPlainObject(body) ? body["error"] : undefined;
8
+ const details = isPlainObject(error) ? error["details"] : undefined;
9
+ const maxRows = isPlainObject(details) ? details["maxRows"] : undefined;
10
+ return typeof maxRows === "number" ? maxRows : undefined;
11
+ }
12
+ catch {
13
+ return undefined;
14
+ }
15
+ }
16
+ export async function requestXlsxExport(source) {
17
+ const headers = { "Content-Type": "application/json" };
18
+ const csrf = readCsrfToken();
19
+ if (csrf !== undefined)
20
+ headers[CSRF_HEADER_NAME] = csrf;
21
+ const locale = readActiveLocale();
22
+ if (locale !== undefined)
23
+ headers[LOCALE_HEADER_NAME] = locale;
24
+ try {
25
+ const res = await fetch("/api/export/xlsx", {
26
+ method: "POST",
27
+ credentials: "include",
28
+ headers,
29
+ body: JSON.stringify({
30
+ screen: source.screen,
31
+ payload: source.payload,
32
+ columns: source.columns,
33
+ timeZone: Intl.DateTimeFormat().resolvedOptions().timeZone,
34
+ }),
35
+ });
36
+ if (res.ok)
37
+ return { status: "ok", blob: await res.blob() };
38
+ if (res.status === TOO_MANY_ROWS_HTTP_STATUS) {
39
+ return { status: "too-many", maxRows: await readMaxRows(res) };
40
+ }
41
+ return { status: "failed" };
42
+ }
43
+ catch {
44
+ return { status: "failed" };
45
+ }
46
+ }
@@ -0,0 +1,16 @@
1
+ import type { CSSProperties, ReactNode } from "react";
2
+ export declare const ACTIONS_CONTENT_ATTRIBUTE = "data-actions-content";
3
+ export declare function stickyActionsCellClass(fillsHeight: boolean): string;
4
+ export declare function ActionsHeaderCell({ label, style, absorbsLeftover, }: {
5
+ readonly label: string;
6
+ readonly style?: CSSProperties | undefined;
7
+ /** Fixed table layout: `w-px` would pin this column to 1px, so it must size as auto and take the leftover width. */
8
+ readonly absorbsLeftover: boolean;
9
+ }): ReactNode;
10
+ export declare function ActionsBodyCell({ testId, fillsHeight, measureContent, children, }: {
11
+ readonly testId: string;
12
+ readonly fillsHeight: boolean;
13
+ /** Wraps the content so the column's natural width stays measurable under a fixed table layout. */
14
+ readonly measureContent: boolean;
15
+ readonly children: ReactNode;
16
+ }): ReactNode;
@@ -0,0 +1,35 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { cn } from "../../lib/cn.js";
3
+ import { TableCell, TableHead } from "../../ui/table.js";
4
+ import { ROW_FOCUS_RING_CELL_CLASS } from "./use-row-keyboard-navigation.js";
5
+ export const ACTIONS_CONTENT_ATTRIBUTE = "data-actions-content";
6
+ export function stickyActionsCellClass(fillsHeight) {
7
+ return cn("md:sticky md:right-0 md:z-10 md:bg-background", fillsHeight && "md:bg-card");
8
+ }
9
+ export function ActionsHeaderCell({ label, style, absorbsLeftover, }) {
10
+ return (_jsx(TableHead, { "data-testid": "column-actions",
11
+ // From md: sticky right-0 + bg-muted (= header tone) so the
12
+ // action column stays at the right edge during horizontal
13
+ // scroll on wider screens. Below md, actions scroll in
14
+ // normal flow — sticky there would pin the column over the
15
+ // preceding data column instead of the (already narrow)
16
+ // page edge. No border-l: a permanent divider looks heavy;
17
+ // the sticky bg already sets the column apart during scroll.
18
+ className: "w-px text-right text-muted-foreground md:sticky md:right-0 md:z-10 md:bg-muted", "aria-label": label, style: absorbsLeftover ? { ...style, width: "auto" } : style }));
19
+ }
20
+ export function ActionsBodyCell({ testId, fillsHeight, measureContent, children, }) {
21
+ return (_jsx(TableCell, { "data-testid": testId,
22
+ // From md: sticky-right so the actions stay visible on the
23
+ // right edge during horizontal scroll. Below md, actions
24
+ // scroll with the row like any other cell — sticky there
25
+ // would hide the neighboring data column on narrow
26
+ // viewports. bg-background sets the column apart during
27
+ // scroll — no border-l (divider too heavy).
28
+ className: cn("whitespace-nowrap text-right", stickyActionsCellClass(fillsHeight), ROW_FOCUS_RING_CELL_CLASS),
29
+ // Only under column sizing is the empty cell leftover width that belongs to the row;
30
+ // otherwise it is padding around the buttons, where a misclick must not open the row.
31
+ onClick: (e) => {
32
+ if (!measureContent || e.target !== e.currentTarget)
33
+ e.stopPropagation();
34
+ }, onKeyDown: (e) => e.stopPropagation(), children: measureContent ? (_jsx("div", { [ACTIONS_CONTENT_ATTRIBUTE]: "", className: "ml-auto w-max", children: children })) : (children) }));
35
+ }
@@ -0,0 +1,20 @@
1
+ import type { ReactNode } from "react";
2
+ import type { DataTableBulkAction } from "../../../data-table-bulk-types.ts";
3
+ import type { Translate } from "../../../headless/index.ts";
4
+ import type { BulkRunState } from "../../../hooks/use-bulk-run.ts";
5
+ export declare function BulkSelectionBar({ flush, actions, selectedCount, isFull, runState, resultActionLabel, pendingAction, t, onRequest, onConfirm, onConfirmClose, onCancel, onClear, onDismissResult, }: {
6
+ readonly flush: boolean;
7
+ readonly actions: readonly DataTableBulkAction[];
8
+ readonly selectedCount: number;
9
+ readonly isFull: boolean;
10
+ readonly runState: BulkRunState;
11
+ readonly resultActionLabel: string;
12
+ readonly pendingAction: DataTableBulkAction | undefined;
13
+ readonly t: Translate;
14
+ readonly onRequest: (action: DataTableBulkAction) => void;
15
+ readonly onConfirm: (action: DataTableBulkAction) => void;
16
+ readonly onConfirmClose: () => void;
17
+ readonly onCancel: () => void;
18
+ readonly onClear: () => void;
19
+ readonly onDismissResult: () => void;
20
+ }): ReactNode;
@@ -0,0 +1,37 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { MAX_BULK_SELECTION } from "@cosmicdrift/kumiko-framework/ui-types";
3
+ import { cn } from "../../lib/cn.js";
4
+ import { DefaultBanner } from "../banner.js";
5
+ import { DefaultButton } from "../button.js";
6
+ import { DefaultDialog } from "../dialog.js";
7
+ const BAR_CLASS = "flex flex-wrap items-center gap-2 bg-muted/60 py-2 text-sm text-foreground";
8
+ // A scrollBody table has no frame of its own, so the bar spans it like the toolbar does.
9
+ const FLUSH_BAR_CLASS = "border-t px-6 max-md:px-3";
10
+ const FRAMED_BAR_CLASS = "rounded-lg border px-3";
11
+ function ResultBanner({ result, actionLabel, t, onDismiss, }) {
12
+ const tallies = [
13
+ { key: "succeeded", text: "kumiko.list.bulk.result.succeeded", ids: result.succeededIds },
14
+ { key: "failed", text: "kumiko.list.bulk.result.failed", ids: result.failedIds },
15
+ { key: "skipped", text: "kumiko.list.bulk.result.skipped", ids: result.skippedIds },
16
+ { key: "not-started", text: "kumiko.list.bulk.result.not-started", ids: result.notStartedIds },
17
+ ];
18
+ return (_jsxs(DefaultBanner, { variant: result.failedIds.length > 0 ? "warning" : "info", title: t("kumiko.list.bulk.result.title", { action: actionLabel }), testId: "bulk-result", actions: _jsx(DefaultButton, { variant: "secondary", size: "sm", onClick: onDismiss, testId: "bulk-result-dismiss", children: t("kumiko.list.bulk.result.dismiss") }), children: [_jsx("ul", { className: "m-0 flex list-none flex-wrap gap-x-4 p-0", children: tallies
19
+ .filter((tally) => tally.key === "succeeded" || tally.ids.length > 0)
20
+ .map((tally) => (_jsx("li", { "data-testid": `bulk-result-${tally.key}`, children: t(tally.text, { count: tally.ids.length }) }, tally.key))) }), result.firstFailureMessage !== undefined && (_jsx("p", { "data-testid": "bulk-result-error", className: "m-0 mt-1 text-foreground-secondary", children: result.firstFailureMessage }))] }));
21
+ }
22
+ function RunProgress({ state, t, onCancel, barClassName, }) {
23
+ return (_jsxs("div", { "data-testid": "bulk-bar", "data-phase": "running", className: barClassName, children: [_jsx("span", { "data-testid": "bulk-progress", className: "tabular-nums", "aria-live": "polite", children: t("kumiko.list.bulk.progress", { settled: state.settled, total: state.total }) }), _jsx(DefaultButton, { variant: "secondary", size: "sm", onClick: onCancel, disabled: state.cancelRequested, testId: "bulk-cancel", className: "ms-auto", children: t(state.cancelRequested ? "kumiko.list.bulk.cancelling" : "kumiko.list.bulk.cancel") })] }));
24
+ }
25
+ export function BulkSelectionBar({ flush, actions, selectedCount, isFull, runState, resultActionLabel, pendingAction, t, onRequest, onConfirm, onConfirmClose, onCancel, onClear, onDismissResult, }) {
26
+ const confirmText = pendingAction?.confirmText?.(selectedCount);
27
+ // skip: nothing selected, no run and no result leaves no bar in the DOM
28
+ if (runState.phase === "idle" && selectedCount === 0 && pendingAction === undefined)
29
+ return null;
30
+ const barClassName = cn(BAR_CLASS, flush ? FLUSH_BAR_CLASS : FRAMED_BAR_CLASS);
31
+ return (_jsxs("div", { className: flush ? "flex flex-col" : "flex flex-col gap-2", children: [runState.phase === "finished" && (_jsx("div", { className: flush ? "px-6 pb-2 max-md:px-3" : undefined, children: _jsx(ResultBanner, { result: runState.result, actionLabel: resultActionLabel, t: t, onDismiss: onDismissResult }) })), runState.phase === "running" ? (_jsx(RunProgress, { state: runState, t: t, onCancel: onCancel, barClassName: barClassName })) : (selectedCount > 0 && (_jsxs("div", { "data-testid": "bulk-bar", "data-phase": "idle", className: barClassName, children: [_jsx("span", { "data-testid": "bulk-selected-count", className: "font-medium tabular-nums", children: t("kumiko.list.bulk.selected", { count: selectedCount }) }), actions.map((action) => (_jsx(DefaultButton, { variant: action.style === "danger" ? "danger" : "secondary", size: "sm", onClick: () => onRequest(action), testId: `bulk-action-${action.id}`, ...(action.icon !== undefined && { icon: action.icon }), children: action.label }, action.id))), isFull && (_jsx("span", { "data-testid": "bulk-limit", className: "text-foreground-secondary", children: t("kumiko.list.bulk.limit", { max: MAX_BULK_SELECTION }) })), _jsx(DefaultButton, { variant: "ghost", size: "sm", onClick: onClear, testId: "bulk-clear", className: "ms-auto", children: t("kumiko.list.bulk.clear") })] }))), pendingAction !== undefined && (_jsx(DefaultDialog, { open: true, onOpenChange: (open) => {
32
+ if (!open)
33
+ onConfirmClose();
34
+ }, title: pendingAction.label, ...(confirmText !== undefined && { description: confirmText }), confirmLabel: pendingAction.confirmLabel ?? pendingAction.label, variant: pendingAction.style === "danger" ? "danger" : "default",
35
+ // The run must not hold the dialog open: the bar shows its progress.
36
+ onConfirm: () => onConfirm(pendingAction), testId: "bulk-confirm" }))] }));
37
+ }
@@ -0,0 +1,6 @@
1
+ import type { cardColumnRoles } from "@cosmicdrift/kumiko-framework/ui-types";
2
+ import type { ReactNode } from "react";
3
+ import type { ListColumnViewModel, ListRowViewModel } from "../../../headless/index.ts";
4
+ import type { DataTableModel } from "./use-data-table-state.ts";
5
+ export type CardRoles = ReturnType<typeof cardColumnRoles<ListColumnViewModel>>;
6
+ export declare function renderCardBody(m: DataTableModel, row: ListRowViewModel, roles: CardRoles): ReactNode;
@@ -0,0 +1,32 @@
1
+ import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { ChevronRight } from "lucide-react";
3
+ import { cellLinkProp, DataTableCell, isBadgeColumn, isEmptyCellValue, isUnlabeledFalse, showsBareCheckMark, } from "../data-table-cell.js";
4
+ import { CardLeadingImage } from "../file-thumbnail.js";
5
+ import { columnCellPresentationProps } from "./data-cell-props.js";
6
+ import { showFullTextOnHover } from "./table-footer.js";
7
+ function cardCell(m, row, col) {
8
+ const { onCellChange } = m;
9
+ // A bare check mark in the subtitle line says nothing without its column.
10
+ if (showsBareCheckMark(col.type, row.values[col.field], col.renderer))
11
+ return col.label;
12
+ return (_jsx(DataTableCell, { value: row.values[col.field], row: row.values, field: col.field, type: col.type, renderer: col.renderer, translate: m.tableTranslate, locale: m.tableLocale, ...columnCellPresentationProps(col), ...(col.fileDisplay !== undefined && { fileDisplay: col.fileDisplay, compactFile: true }), ...(onCellChange !== undefined && {
13
+ onChange: (value) => onCellChange(row.id, col.field, value),
14
+ }), ...cellLinkProp(col, row, m.rowOpener(row) === undefined) }));
15
+ }
16
+ function cellTestIdOf(m, row, field) {
17
+ return m.getCellTestId?.(row, field) ?? `cell-${row.id}-${field}`;
18
+ }
19
+ function renderCardMeta(m, row, metaColumns) {
20
+ return (_jsx("div", { className: "min-w-0 overflow-x-clip", children: _jsx("div", { "data-testid": `card-meta-${row.id}`, className: "-ms-3 flex min-w-0 flex-wrap items-center text-[13px] leading-5 tabular-nums text-foreground-secondary", children: metaColumns.map((col) => (_jsxs("div", { className: "flex h-5 min-w-0 max-w-full items-center", children: [_jsx("span", { "aria-hidden": "true", className: "w-3 shrink-0 text-center", children: "\u00B7" }), _jsx("div", { "data-testid": cellTestIdOf(m, row, col.field), className: "min-w-0 truncate leading-5", onPointerEnter: showFullTextOnHover, children: isBadgeColumn(col) ? (_jsx("span", { className: "inline-flex align-middle", children: cardCell(m, row, col) })) : (cardCell(m, row, col)) })] }, col.field))) }) }));
21
+ }
22
+ function renderCardHeading(m, row, roles) {
23
+ const { title: cardTitleColumn, status: cardStatusColumn } = roles;
24
+ const showStatus = cardStatusColumn !== undefined && !isEmptyCellValue(row.values[cardStatusColumn.field]);
25
+ return (_jsxs("div", { className: "flex min-w-0 items-start justify-between gap-2", children: [cardTitleColumn !== undefined && (_jsx("span", { "data-testid": cellTestIdOf(m, row, cardTitleColumn.field), className: "min-w-0 truncate text-base font-semibold text-foreground", onPointerEnter: showFullTextOnHover, children: cardCell(m, row, cardTitleColumn) })), showStatus && (_jsx("span", { "data-testid": cellTestIdOf(m, row, cardStatusColumn.field), className: "shrink-0", children: cardCell(m, row, cardStatusColumn) }))] }));
26
+ }
27
+ export function renderCardBody(m, row, roles) {
28
+ const { image: cardImageColumn, meta: cardMetaColumns } = roles;
29
+ const metaColumns = cardMetaColumns.filter((col) => !isEmptyCellValue(row.values[col.field]) &&
30
+ !isUnlabeledFalse(col.type, row.values[col.field], col.renderer));
31
+ return (_jsxs(_Fragment, { children: [cardImageColumn?.fileDisplay !== undefined && (_jsx("div", { "data-testid": `card-image-${row.id}`, className: "shrink-0", children: _jsx(CardLeadingImage, { value: row.values[cardImageColumn.field], display: cardImageColumn.fileDisplay }) })), _jsxs("div", { className: "flex min-w-0 flex-1 flex-col gap-1", children: [renderCardHeading(m, row, roles), metaColumns.length > 0 && renderCardMeta(m, row, metaColumns)] }), m.rowOpener(row) !== undefined && (_jsx(ChevronRight, { className: "size-4 shrink-0 text-muted-foreground", "aria-hidden": "true" }))] }));
32
+ }
@@ -0,0 +1,5 @@
1
+ import { type ReactNode } from "react";
2
+ import type { ListRowViewModel } from "../../../headless/index.ts";
3
+ import { type CardRoles } from "./card-body.tsx";
4
+ import type { DataTableModel } from "./use-data-table-state.ts";
5
+ export declare function renderCard(m: DataTableModel, row: ListRowViewModel, roles: CardRoles): ReactNode;
@@ -0,0 +1,31 @@
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 { rowToneClass } from "../data-table-cell.js";
5
+ import { RowActionsCell } from "../row-actions.js";
6
+ import { renderCardBody } from "./card-body.js";
7
+ import { renderExpandToggle, renderExpansion } from "./row-render-parts.js";
8
+ function renderCardMenu(m, row, menuActions) {
9
+ return (_jsx("div", { className: "shrink-0", onClick: (e) => e.stopPropagation(), onKeyDown: (e) => e.stopPropagation(), children: _jsx(RowActionsCell, { row: row, actions: menuActions, mode: m.rowActionMode, rowIsLink: m.onRowClick !== undefined, compact: true }) }));
10
+ }
11
+ function renderCardOpenButton(openRow, body) {
12
+ return (_jsx("button", { type: "button", className: "flex min-w-0 flex-1 cursor-pointer items-center gap-2 bg-transparent p-0 text-left", onKeyDown: (e) => {
13
+ if (e.key !== "Enter")
14
+ return;
15
+ e.preventDefault();
16
+ e.stopPropagation();
17
+ openRow();
18
+ }, children: body }));
19
+ }
20
+ export function renderCard(m, row, roles) {
21
+ const { rowTone, menuActions } = m;
22
+ const hasMenu = menuActions !== undefined && menuActions.length > 0;
23
+ const openRow = m.rowOpener(row);
24
+ const body = renderCardBody(m, row, roles);
25
+ const rowItem = (_jsxs("li", { "data-testid": m.rowTestId(row), "data-tone": rowTone?.(row), className: cn("flex min-h-[72px] items-center gap-2 border-b border-border-row py-3", rowToneClass(rowTone?.(row)), m.expansion !== undefined || m.selection.active ? "pl-2" : "pl-4", hasMenu ? "pr-2" : "pr-4", m.density.card), ...(openRow !== undefined && { onClick: openRow }), children: [m.selection.cardCheckbox(row), renderExpandToggle(m, row), openRow !== undefined ? renderCardOpenButton(openRow, body) : body, hasMenu && renderCardMenu(m, row, menuActions)] }, row.id));
26
+ if (m.expansion === undefined)
27
+ return rowItem;
28
+ // The expansion is a sibling <li>, outside the row's click target, so
29
+ // clicks in the sub-area never navigate the parent row.
30
+ return (_jsxs(Fragment, { children: [rowItem, renderExpansion(m, row, "item", 0)] }, row.id));
31
+ }
@@ -0,0 +1,14 @@
1
+ import type { ReactNode } from "react";
2
+ import type { DataTableSort } from "../../../index.ts";
3
+ type SortableColumn = {
4
+ readonly field: string;
5
+ readonly label: string;
6
+ readonly sortable?: boolean;
7
+ };
8
+ export declare function CardSortSelect({ columns, sort, onSortChange, testId, }: {
9
+ readonly columns: readonly SortableColumn[];
10
+ readonly sort: DataTableSort | null | undefined;
11
+ readonly onSortChange: ((next: DataTableSort | null) => void) | undefined;
12
+ readonly testId: string | undefined;
13
+ }): ReactNode;
14
+ export {};
@@ -0,0 +1,40 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { ArrowDown, ArrowUp } from "lucide-react";
3
+ import { useOptionalTranslation } from "../../../index.js";
4
+ // No column headers below the breakpoint, so the click-to-sort affordance
5
+ // on SortableHeader has nothing to attach to. A single native <select> is
6
+ // the whole fix — no custom widget, no menu — fed straight from the
7
+ // columns marked sortable in the ViewModel. Options carry the resolved
8
+ // {field, dir} directly so onChange can look the pick up by value instead
9
+ // of parsing/casting the option string back apart.
10
+ export function CardSortSelect({ columns, sort, onSortChange, testId, }) {
11
+ const translate = useOptionalTranslation();
12
+ const sortableColumns = columns.filter((col) => col.sortable);
13
+ if (onSortChange === undefined || sortableColumns.length === 0)
14
+ return null;
15
+ const sortOptions = sortableColumns.flatMap((col) => [
16
+ { value: `${col.field}:asc`, field: col.field, dir: "asc", label: `${col.label} ↑` },
17
+ { value: `${col.field}:desc`, field: col.field, dir: "desc", label: `${col.label} ↓` },
18
+ ]);
19
+ const sortLabel = sort !== undefined && sort !== null
20
+ ? (translate?.("kumiko.list.sort.by", {
21
+ column: columns.find((col) => col.field === sort.field)?.label ?? sort.field,
22
+ }) ?? `Sorted by ${sort.field}`)
23
+ : (translate?.("kumiko.list.sort.unsorted") ?? "Unsorted");
24
+ const SortArrow = sort?.dir === "desc" ? ArrowDown : ArrowUp;
25
+ // URL state can carry a sort on a column that is not sortable; without an
26
+ // option for it the select would claim "Unsorted" while the list is sorted.
27
+ const currentSortValue = sort !== undefined && sort !== null ? `${sort.field}:${sort.dir}` : "";
28
+ const currentSortIsUnlisted = sort !== undefined && sort !== null && !sortOptions.some((o) => o.value === currentSortValue);
29
+ return (_jsxs("div", { className: "relative flex h-9 items-center gap-1.5 px-4 text-[13px] text-foreground-secondary", children: [_jsx("span", { "aria-hidden": "true", children: sortLabel }), sort !== undefined && sort !== null && _jsx(SortArrow, { className: "size-3.5", "aria-hidden": "true" }), _jsxs("select", { "aria-label": translate?.("kumiko.list.sort.label") ?? "Sort", "data-testid": testId !== undefined ? `${testId}-sort` : "render-list-sort", value: currentSortValue, onChange: (e) => {
30
+ const raw = e.target.value;
31
+ if (raw === "") {
32
+ onSortChange(null);
33
+ return;
34
+ }
35
+ const picked = sortOptions.find((o) => o.value === raw);
36
+ if (picked === undefined)
37
+ return;
38
+ onSortChange({ field: picked.field, dir: picked.dir });
39
+ }, className: "absolute inset-0 size-full cursor-pointer opacity-0", children: [_jsx("option", { value: "", children: translate?.("kumiko.list.sort.unsorted") ?? "Unsorted" }), currentSortIsUnlisted && (_jsx("option", { value: currentSortValue, children: `${columns.find((col) => col.field === sort.field)?.label ?? sort.field} ${sort.dir === "desc" ? "↓" : "↑"}` })), sortOptions.map((o) => (_jsx("option", { value: o.value, children: o.label }, o.value)))] })] }));
40
+ }
@@ -0,0 +1,3 @@
1
+ import type { ReactNode } from "react";
2
+ import type { DataTableModel } from "./use-data-table-state.ts";
3
+ export declare function renderCardsView(m: DataTableModel): ReactNode;
@@ -0,0 +1,13 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { cardColumnRoles } from "@cosmicdrift/kumiko-framework/ui-types";
3
+ import { cn } from "../../lib/cn.js";
4
+ import { renderCard } from "./card-row.js";
5
+ import { CardSortSelect } from "./card-sort-select.js";
6
+ import { bandClassName, bandReactKey, bandTestId } from "./group-band.js";
7
+ import { renderBand } from "./row-render-parts.js";
8
+ import { TotalsCard } from "./totals-footer.js";
9
+ export function renderCardsView(m) {
10
+ const { testId, totals, columns } = m;
11
+ const roles = cardColumnRoles(columns);
12
+ return (_jsx("div", { "data-testid": testId, className: "contents", children: _jsxs("div", { "data-testid": testId !== undefined ? `${testId}-cards` : "render-list-cards", className: "flex flex-col", children: [_jsx(CardSortSelect, { columns: columns, sort: m.sort, onSortChange: m.onSortChange, testId: testId }), _jsx("ul", { className: "m-0 list-none border-t border-border-row p-0", children: m.tableItems.map((item) => item.kind === "row" ? (renderCard(m, item.row, roles)) : (_jsx("li", { "data-testid": bandTestId(item), className: cn("border-b border-border-row", bandClassName(item)), children: renderBand(m, item) }, bandReactKey(item)))) }), totals !== undefined && _jsx(TotalsCard, { totals: totals, columnOf: m.columnOf })] }) }));
13
+ }
@@ -0,0 +1,5 @@
1
+ import type { DataTableViewOptions } from "../../../data-table-view-types.ts";
2
+ /** Pinning, resizing and keyboard navigation only apply to the table layout (md and up). */
3
+ export declare function isPinningActive(view: DataTableViewOptions | undefined, isNarrow: boolean): boolean;
4
+ export declare function isResizingActive(view: DataTableViewOptions | undefined, isNarrow: boolean): boolean;
5
+ export declare function isKeyboardNavigationActive(view: DataTableViewOptions | undefined, isNarrow: boolean): boolean;
@@ -0,0 +1,10 @@
1
+ /** Pinning, resizing and keyboard navigation only apply to the table layout (md and up). */
2
+ export function isPinningActive(view, isNarrow) {
3
+ return !isNarrow && view?.pins !== undefined;
4
+ }
5
+ export function isResizingActive(view, isNarrow) {
6
+ return !isNarrow && view?.widths !== undefined && view.minWidths !== undefined;
7
+ }
8
+ export function isKeyboardNavigationActive(view, isNarrow) {
9
+ return !isNarrow && view?.keyboardNavigation === true;
10
+ }
@@ -0,0 +1,7 @@
1
+ import type { ReactNode } from "react";
2
+ import type { DataTableViewColumn } from "../../../data-table-view-types.ts";
3
+ export declare function ColumnPickerSection({ columns, labels, onChange, }: {
4
+ readonly columns: readonly DataTableViewColumn[];
5
+ readonly labels: ReadonlyMap<string, string>;
6
+ readonly onChange: (next: readonly DataTableViewColumn[]) => void;
7
+ }): ReactNode;
@@ -0,0 +1,35 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { ArrowDown, ArrowUp, GripVertical } from "lucide-react";
3
+ import { useOptionalTranslation } from "../../../index.js";
4
+ import { Button as UiButton } from "../../ui/button.js";
5
+ import { Checkbox } from "../../ui/checkbox.js";
6
+ import { usePointerDrag } from "../../widgets/use-pointer-drag.js";
7
+ const ROW_HEIGHT_PX = 32;
8
+ function moveColumn(columns, from, to) {
9
+ const target = Math.max(0, Math.min(columns.length - 1, to));
10
+ if (target === from)
11
+ return columns;
12
+ const next = [...columns];
13
+ const [moved] = next.splice(from, 1);
14
+ if (moved !== undefined)
15
+ next.splice(target, 0, moved);
16
+ return next;
17
+ }
18
+ function PickerRow({ columns, index, label, isLastVisible, onChange }) {
19
+ const translate = useOptionalTranslation();
20
+ const column = columns[index];
21
+ // Positions are relative to the order at pointer-down, so every move is idempotent.
22
+ const drag = usePointerDrag({
23
+ onStart: () => ({ snapshot: columns, from: index }),
24
+ onMove: (start, { dy }) => onChange(moveColumn(start.snapshot, start.from, start.from + Math.round(dy / ROW_HEIGHT_PX))),
25
+ });
26
+ if (column === undefined)
27
+ return null;
28
+ const checkboxId = `list-view-column-${column.field}`;
29
+ return (_jsxs("li", { className: "flex h-8 items-center gap-1.5", "data-testid": `list-view-column-row-${column.field}`, children: [_jsx("span", { ...drag, "aria-hidden": "true", className: "flex size-6 shrink-0 cursor-grab touch-none items-center justify-center text-muted-foreground", children: _jsx(GripVertical, { className: "size-4" }) }), _jsx(Checkbox, { id: checkboxId, checked: column.visible, disabled: column.visible && isLastVisible, onCheckedChange: (checked) => onChange(columns.map((c, i) => (i === index ? { ...c, visible: checked === true } : c))) }), _jsx("label", { htmlFor: checkboxId, className: "min-w-0 flex-1 truncate text-sm", children: label }), _jsx(UiButton, { type: "button", variant: "ghost", size: "icon", className: "size-6", disabled: index === 0, "aria-label": translate?.("kumiko.list.view.column.move-up", { column: label }) ?? `Move ${label} up`, onClick: () => onChange(moveColumn(columns, index, index - 1)), children: _jsx(ArrowUp, { className: "size-3.5", "aria-hidden": "true" }) }), _jsx(UiButton, { type: "button", variant: "ghost", size: "icon", className: "size-6", disabled: index === columns.length - 1, "aria-label": translate?.("kumiko.list.view.column.move-down", { column: label }) ??
30
+ `Move ${label} down`, onClick: () => onChange(moveColumn(columns, index, index + 1)), children: _jsx(ArrowDown, { className: "size-3.5", "aria-hidden": "true" }) })] }));
31
+ }
32
+ export function ColumnPickerSection({ columns, labels, onChange, }) {
33
+ const visibleCount = columns.filter((column) => column.visible).length;
34
+ return (_jsx("ul", { className: "m-0 flex list-none flex-col p-0", "data-testid": "list-view-columns", children: columns.map((column, index) => (_jsx(PickerRow, { columns: columns, index: index, label: labels.get(column.field) ?? column.field, isLastVisible: visibleCount === 1, onChange: onChange }, column.field))) }));
35
+ }
@@ -0,0 +1,14 @@
1
+ import { type ReactNode } from "react";
2
+ import type { ColumnSizing } from "./use-column-sizing.ts";
3
+ export declare function ColumnWidthGroup({ fields, sizing, hasExpandColumn, hasSelectColumn, hasActionsColumn, }: {
4
+ readonly fields: readonly string[];
5
+ readonly sizing: ColumnSizing;
6
+ readonly hasExpandColumn: boolean;
7
+ readonly hasSelectColumn?: boolean;
8
+ readonly hasActionsColumn: boolean;
9
+ }): ReactNode;
10
+ export declare function ColumnResizeHandle({ field, label, sizing, }: {
11
+ readonly field: string;
12
+ readonly label: string;
13
+ readonly sizing: ColumnSizing;
14
+ }): ReactNode;
@@ -0,0 +1,48 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useRef } from "react";
3
+ import { MAX_COLUMN_WIDTH_PX } from "../../../hooks/list-column-layout.js";
4
+ import { useOptionalTranslation } from "../../../index.js";
5
+ import { arrowKeyDelta, usePointerDrag } from "../../widgets/use-pointer-drag.js";
6
+ import { EXPAND_COLUMN_WIDTH_PX, SELECT_COLUMN_WIDTH_PX } from "./use-measured-column-widths.js";
7
+ export function ColumnWidthGroup({ fields, sizing, hasExpandColumn, hasSelectColumn = false, hasActionsColumn, }) {
8
+ return (_jsxs("colgroup", { children: [hasSelectColumn && _jsx("col", { style: { width: SELECT_COLUMN_WIDTH_PX } }), hasExpandColumn && _jsx("col", { style: { width: EXPAND_COLUMN_WIDTH_PX } }), fields.map((field) => (_jsx("col", { style: { width: sizing.widthOf(field) } }, field))), hasActionsColumn && _jsx("col", {})] }));
9
+ }
10
+ // Visible while the header is hovered or the grip has focus; the wider 8px hit area stays transparent.
11
+ const HANDLE_CLASS = "absolute inset-y-0 right-0 z-10 m-0 h-full w-2 border-0 bg-transparent cursor-col-resize touch-none outline-none " +
12
+ "after:absolute after:inset-y-1 after:right-0 after:w-0.5 after:rounded-full after:bg-transparent " +
13
+ "group-hover/header:after:bg-border hover:after:bg-ring focus-visible:after:bg-ring active:after:bg-ring";
14
+ export function ColumnResizeHandle({ field, label, sizing, }) {
15
+ const translate = useOptionalTranslation();
16
+ const width = sizing.widthOf(field);
17
+ const minWidth = sizing.minWidthOf(field);
18
+ const draggedWidth = useRef(null);
19
+ const clampToBounds = (value) => Math.min(MAX_COLUMN_WIDTH_PX, Math.max(minWidth, value));
20
+ const drag = usePointerDrag({
21
+ onStart: () => width,
22
+ onMove: (startWidth, { dx }) => {
23
+ const next = clampToBounds(startWidth + dx);
24
+ draggedWidth.current = next;
25
+ sizing.setLiveWidth(field, next);
26
+ },
27
+ onEnd: () => {
28
+ const final = draggedWidth.current;
29
+ draggedWidth.current = null;
30
+ if (final !== null)
31
+ sizing.commit(field, final);
32
+ },
33
+ });
34
+ const onKeyDown = (event) => {
35
+ event.stopPropagation();
36
+ const delta = arrowKeyDelta(event);
37
+ if (delta === undefined || delta.dx === 0)
38
+ return;
39
+ event.preventDefault();
40
+ sizing.commit(field, clampToBounds(width + delta.dx));
41
+ };
42
+ return (_jsx("hr", { "aria-orientation": "vertical", "aria-valuenow": Math.round(width), "aria-valuemin": minWidth, "aria-valuemax": MAX_COLUMN_WIDTH_PX,
43
+ // title, not aria-label: getByLabel("<column>") would match a resize handle for every column.
44
+ title: translate?.("kumiko.list.column.resize", { column: label }) ?? `Resize ${label}`, tabIndex: 0, "data-testid": `column-resize-${field}`, className: HANDLE_CLASS, onClick: (event) => event.stopPropagation(), onKeyDown: onKeyDown, ...drag, onPointerDown: (event) => {
45
+ event.stopPropagation();
46
+ drag.onPointerDown(event);
47
+ } }));
48
+ }
@@ -0,0 +1,7 @@
1
+ import type { ListColumnViewModel } from "../../../headless/index.ts";
2
+ /** Optional presentation props every DataTableCell gets from its column. */
3
+ export declare function columnCellPresentationProps(col: ListColumnViewModel): {
4
+ optionLabels?: Readonly<Record<string, string>> | undefined;
5
+ optionTones?: Readonly<Partial<Record<string, "bad" | "neutral" | "ok" | "warn">>> | undefined;
6
+ grouping?: boolean | undefined;
7
+ };
@@ -0,0 +1,8 @@
1
+ /** Optional presentation props every DataTableCell gets from its column. */
2
+ export function columnCellPresentationProps(col) {
3
+ return {
4
+ ...(col.optionLabels !== undefined && { optionLabels: col.optionLabels }),
5
+ ...(col.optionTones !== undefined && { optionTones: col.optionTones }),
6
+ ...(col.grouping !== undefined && { grouping: col.grouping }),
7
+ };
8
+ }