@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,11 @@
1
+ import type { StackedAreaGeometry } from "./stacked-area-geometry.ts";
2
+ import type { ResolvedStackedAreaProps } from "./stacked-area-props.ts";
3
+ import type { useStackedAreaTooltip } from "./use-stacked-area-tooltip.ts";
4
+ import type { useStackedAreaWindow } from "./use-stacked-area-window.ts";
5
+ export type StackedAreaModel = {
6
+ readonly props: ResolvedStackedAreaProps;
7
+ readonly clipId: string;
8
+ readonly win: ReturnType<typeof useStackedAreaWindow>;
9
+ readonly tooltip: ReturnType<typeof useStackedAreaTooltip>;
10
+ readonly geometry: StackedAreaGeometry;
11
+ };
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,3 @@
1
+ import type { ReactNode } from "react";
2
+ import type { StackedAreaModel } from "./stacked-area-model.ts";
3
+ export declare function renderStackedAreaPlot(m: StackedAreaModel): ReactNode;
@@ -0,0 +1,51 @@
1
+ import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { cn } from "../lib/cn.js";
3
+ import { percent } from "./chart-data.js";
4
+ import { MarkerPins } from "./chart-markers.js";
5
+ import { AXIS_TIERS } from "./stacked-area-geometry.js";
6
+ import { renderPlotSvg } from "./stacked-area-svg.js";
7
+ import { renderTooltipOverlay } from "./stacked-area-tooltip.js";
8
+ const MARKER_LABEL_FLIP_FRACTION = 0.97;
9
+ function bucketLabelTransform(index, count) {
10
+ if (index === 0)
11
+ return "";
12
+ return index === count - 1 ? "-translate-x-full" : "-translate-x-1/2";
13
+ }
14
+ // Markers sharing a timestamp become one label ("A, B") in the first one's color.
15
+ function groupMarkerLabels(markers) {
16
+ const groups = new Map();
17
+ for (const marker of [...markers].sort((a, b) => a.atMs - b.atMs)) {
18
+ groups.set(marker.atMs, [...(groups.get(marker.atMs) ?? []), marker]);
19
+ }
20
+ return [...groups.entries()].map(([atMs, members]) => ({
21
+ atMs,
22
+ text: members.map((m) => m.label).join(", "),
23
+ color: members[0]?.color ?? "var(--color-foreground)",
24
+ }));
25
+ }
26
+ function renderTodayLabel(m, todayFraction) {
27
+ return (_jsx("span", { "data-testid": "chart-today-label", className: "absolute -top-4 -translate-x-1/2 text-[11px] text-foreground", style: { left: percent(todayFraction) }, children: m.props.todayLabel }));
28
+ }
29
+ function renderScrollableAxis(m) {
30
+ const { times, fractionOf, formatBucket } = m.win;
31
+ return (_jsx("div", { "data-testid": "chart-bucket-labels", className: "relative mt-1 h-4 text-[11px] text-muted-foreground", children: times.map((atMs, i) => (_jsx("span", { className: cn("absolute whitespace-nowrap", bucketLabelTransform(i, times.length)), style: { left: percent(fractionOf(atMs)) }, children: formatBucket(atMs) }, atMs))) }));
32
+ }
33
+ function renderTickedAxis(m) {
34
+ const { fractionOf, formatBucket, windowStartMs, windowEndMs, tierTicks } = m.win;
35
+ return (_jsxs("div", { className: "relative mt-1 h-4 text-[11px] text-muted-foreground", children: [_jsx("span", { className: "absolute left-0 whitespace-nowrap", children: formatBucket(windowStartMs) }), _jsx("span", { className: "absolute right-0 whitespace-nowrap", children: formatBucket(windowEndMs) }), AXIS_TIERS.map((tier, tierIndex) => (_jsx("div", { className: cn("absolute inset-0", tier.visibilityClass), children: (tierTicks[tierIndex] ?? []).map((tickMs) => (_jsx("span", { "data-testid": "chart-axis-tick", className: "absolute -translate-x-1/2 whitespace-nowrap", style: { left: percent(fractionOf(tickMs)) }, children: formatBucket(tickMs) }, tickMs))) }, tier.maxLabels)))] }));
36
+ }
37
+ function renderMarkerLabels(m) {
38
+ return groupMarkerLabels(m.win.markers ?? []).map((group) => {
39
+ const fraction = m.win.fractionOf(group.atMs);
40
+ const flipped = fraction > MARKER_LABEL_FLIP_FRACTION;
41
+ return (_jsx("div", { className: cn("pointer-events-none absolute top-0 hidden @2xl:block", flipped ? "-translate-x-full pr-1" : "pl-1"), style: { left: percent(fraction) }, children: _jsx("span", { "data-testid": "chart-marker-label", className: "block max-h-44 rotate-180 overflow-hidden text-ellipsis whitespace-nowrap text-[11px] leading-none [writing-mode:vertical-rl] @2xl:max-h-60", style: { color: group.color }, children: group.text }) }, group.atMs));
42
+ });
43
+ }
44
+ function renderMarkerPins(m, markers) {
45
+ const { markerLegend, markerLabels } = m.props;
46
+ return (_jsx(MarkerPins, { markers: markers, windowStartMs: m.win.windowStartMs, windowEndMs: m.win.windowEndMs, numbered: markerLegend === "list" || markerLabels, formatMarkerTime: m.win.formatMarkerTimeInWindow, ...(markerLabels && { className: "@2xl:hidden" }) }));
47
+ }
48
+ export function renderStackedAreaPlot(m) {
49
+ const { todayFraction, markers } = m.win;
50
+ return (_jsxs(_Fragment, { children: [todayFraction !== undefined && renderTodayLabel(m, todayFraction), renderPlotSvg(m), m.props.scrollable ? renderScrollableAxis(m) : renderTickedAxis(m), renderTooltipOverlay(m), m.props.markerLabels && renderMarkerLabels(m), markers !== undefined && markers.length > 0 && renderMarkerPins(m, markers)] }));
51
+ }
@@ -0,0 +1,83 @@
1
+ import type { ReactNode } from "react";
2
+ import type { ChartLine, ChartMarker, ChartSeries, ColorMap, ToneMap } from "./chart-data.ts";
3
+ import type { StackedAreaDateFormat, StackedAreaInitialWindow, StackedAreaRanges, StackedAreaWindowSelection } from "./chart-window.ts";
4
+ export type StackedAreaChartProps = {
5
+ readonly series: readonly ChartSeries[];
6
+ readonly windowStartMs: number;
7
+ readonly windowEndMs: number;
8
+ readonly todayMs?: number;
9
+ readonly tones?: ToneMap;
10
+ readonly colors?: ColorMap;
11
+ readonly markers?: readonly ChartMarker[];
12
+ /** Drawn over the bands, not stacked. A `null` value breaks the line. */
13
+ readonly lines?: readonly ChartLine[];
14
+ /** Range presets anchored at today; uncontrolled, the widget renders the switch itself. */
15
+ readonly ranges?: StackedAreaRanges;
16
+ /** Uncontrolled start window; "from-today" ignores the default range and starts at today. */
17
+ readonly initialWindow?: StackedAreaInitialWindow;
18
+ /** Scrubber under the plot to drag the visible window; starts at today without `ranges`. */
19
+ readonly brush?: boolean;
20
+ readonly windowSelection?: StackedAreaWindowSelection;
21
+ /** Makes the window controlled: no own range switch, `windowSelection` is the only source. */
22
+ readonly onWindowSelectionChange?: (selection: StackedAreaWindowSelection) => void;
23
+ /** aria-labels of the two brush handles. */
24
+ readonly brushLabels?: {
25
+ readonly start: string;
26
+ readonly end: string;
27
+ };
28
+ readonly ariaLabel: string;
29
+ readonly todayLabel: string;
30
+ readonly formatBucketLabel: (atMs: number, format: StackedAreaDateFormat) => string;
31
+ readonly formatValue?: (value: number) => string;
32
+ /** y-axis tick labels only (legend and totals keep `formatValue`); defaults to `formatValue`. */
33
+ readonly formatAxisValue?: (value: number) => string;
34
+ readonly formatMarkerTime?: (atMs: number, format: StackedAreaDateFormat) => string;
35
+ /** Overrides the automatic format (month and year once the visible window spans 18 months). */
36
+ readonly dateFormat?: StackedAreaDateFormat;
37
+ /** Zone the x-axis ticks align to; must match the zone `formatBucketLabel` formats in. Default UTC. */
38
+ readonly timeZone?: string;
39
+ /** "legend": unnumbered pins with tooltip, one dashed legend entry per marker label and color. */
40
+ readonly markerLegend?: "list" | "legend";
41
+ /** Writes each marker label vertically next to its guide line on wide containers; narrow ones keep the pins. */
42
+ readonly markerLabels?: boolean;
43
+ /** Fixed width per bucket; the plot scrolls horizontally, y ticks stay put. */
44
+ readonly scrollable?: boolean;
45
+ readonly showLegendTotals?: boolean;
46
+ readonly emptyContent?: ReactNode;
47
+ readonly testId?: string;
48
+ };
49
+ /** formatAxisValue defaults to the resolved formatValue, so the destructuring order matters. */
50
+ export declare function resolveStackedAreaProps({ lines, brush, brushLabels, formatValue, formatAxisValue, markerLegend, markerLabels, scrollable, showLegendTotals, ...rest }: StackedAreaChartProps): {
51
+ series: readonly ChartSeries[];
52
+ windowStartMs: number;
53
+ windowEndMs: number;
54
+ todayMs?: number;
55
+ tones?: ToneMap;
56
+ colors?: ColorMap;
57
+ markers?: readonly ChartMarker[];
58
+ ranges?: StackedAreaRanges;
59
+ initialWindow?: StackedAreaInitialWindow;
60
+ windowSelection?: StackedAreaWindowSelection;
61
+ onWindowSelectionChange?: (selection: StackedAreaWindowSelection) => void;
62
+ ariaLabel: string;
63
+ todayLabel: string;
64
+ formatBucketLabel: (atMs: number, format: StackedAreaDateFormat) => string;
65
+ formatMarkerTime?: (atMs: number, format: StackedAreaDateFormat) => string;
66
+ dateFormat?: StackedAreaDateFormat;
67
+ timeZone?: string;
68
+ emptyContent?: ReactNode;
69
+ testId?: string;
70
+ lines: readonly ChartLine[];
71
+ brush: boolean;
72
+ brushLabels: {
73
+ readonly start: string;
74
+ readonly end: string;
75
+ };
76
+ formatValue: (value: number) => string;
77
+ formatAxisValue: (value: number) => string;
78
+ markerLegend: "legend" | "list";
79
+ markerLabels: boolean;
80
+ scrollable: boolean;
81
+ showLegendTotals: boolean;
82
+ };
83
+ export type ResolvedStackedAreaProps = ReturnType<typeof resolveStackedAreaProps>;
@@ -0,0 +1,16 @@
1
+ const DEFAULT_BRUSH_LABELS = { start: "Window start", end: "Window end" };
2
+ /** formatAxisValue defaults to the resolved formatValue, so the destructuring order matters. */
3
+ export function resolveStackedAreaProps({ lines = [], brush = false, brushLabels = DEFAULT_BRUSH_LABELS, formatValue = String, formatAxisValue = formatValue, markerLegend = "list", markerLabels = false, scrollable = false, showLegendTotals = true, ...rest }) {
4
+ return {
5
+ ...rest,
6
+ lines,
7
+ brush,
8
+ brushLabels,
9
+ formatValue,
10
+ formatAxisValue,
11
+ markerLegend,
12
+ markerLabels,
13
+ scrollable,
14
+ showLegendTotals,
15
+ };
16
+ }
@@ -0,0 +1,3 @@
1
+ import type { ReactNode } from "react";
2
+ import type { StackedAreaModel } from "./stacked-area-model.ts";
3
+ export declare function renderPlotSvg(m: StackedAreaModel): ReactNode;
@@ -0,0 +1,38 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { cn } from "../lib/cn.js";
3
+ import { SINGLE_POINT_RADIUS } from "./chart-data.js";
4
+ import { PLOT_HEIGHT, PLOT_WIDTH, STACKED_AREA_PLOT_HEIGHT_CLASS, } from "./stacked-area-geometry.js";
5
+ const bandGradientId = (m, index) => `${m.clipId}-band-${index}`;
6
+ function todayXOf(m) {
7
+ return (m.win.todayFraction ?? 1) * PLOT_WIDTH;
8
+ }
9
+ function renderBands(m, fillOpacity, edgeOpacity) {
10
+ return m.geometry.bands.map((band, bi) => band.dot === undefined ? (_jsxs("g", { children: [_jsx("path", { d: band.d, fill: `url(#${bandGradientId(m, bi)})`, fillOpacity: fillOpacity, stroke: "none" }), _jsx("path", { "data-testid": `chart-band-edge-${band.key}`, d: band.edge, fill: "none", stroke: band.color, strokeOpacity: edgeOpacity, strokeWidth: 1.5, vectorEffect: "non-scaling-stroke" })] }, band.key)) : (_jsx("circle", { "data-testid": "stacked-area-single-point", cx: PLOT_WIDTH / 2, cy: band.dot, r: SINGLE_POINT_RADIUS, fill: band.color, fillOpacity: fillOpacity }, band.key)));
11
+ }
12
+ function renderDefs(m) {
13
+ const todayX = todayXOf(m);
14
+ return (_jsxs("defs", { children: [m.geometry.bands.map((band, bi) => (_jsxs("linearGradient", { id: bandGradientId(m, bi), x1: "0", x2: "0", y1: "0", y2: "1", children: [_jsx("stop", { offset: "0%", stopColor: band.color, stopOpacity: 1 }), _jsx("stop", { offset: "100%", stopColor: band.color, stopOpacity: 0.35 })] }, band.key))), _jsx("clipPath", { id: `${m.clipId}-past`, children: _jsx("rect", { x: 0, y: 0, width: todayX, height: PLOT_HEIGHT }) }), _jsx("clipPath", { id: `${m.clipId}-future`, children: _jsx("rect", { x: todayX, y: 0, width: PLOT_WIDTH - todayX, height: PLOT_HEIGHT }) })] }));
15
+ }
16
+ function renderGridLines(m) {
17
+ const { yOf, yTickValues } = m.geometry;
18
+ return yTickValues.map((tickValue) => (_jsx("line", { "data-testid": "chart-grid-line", x1: 0, x2: PLOT_WIDTH, y1: yOf(tickValue), y2: yOf(tickValue), stroke: "var(--color-border)", strokeWidth: 1, strokeDasharray: "3 3", vectorEffect: "non-scaling-stroke" }, tickValue)));
19
+ }
20
+ function renderMarkerGuides(m) {
21
+ return m.geometry.coloredMarkers.map((marker) => {
22
+ const x = m.win.fractionOf(marker.atMs) * PLOT_WIDTH;
23
+ return (_jsx("line", { "data-testid": "chart-marker-guide", x1: x, x2: x, y1: 0, y2: PLOT_HEIGHT, stroke: marker.color, strokeWidth: 1, strokeDasharray: "2 3", vectorEffect: "non-scaling-stroke" }, marker.key));
24
+ });
25
+ }
26
+ function renderLinePaths(m) {
27
+ return m.geometry.linePaths.map((line) => (_jsx("path", { "data-testid": `chart-line-${line.key}`, d: line.d, fill: "none", stroke: line.color, strokeWidth: 2, strokeDasharray: line.dashed ? "6 4" : undefined, vectorEffect: "non-scaling-stroke" }, line.key)));
28
+ }
29
+ function renderTodayLine(m) {
30
+ const todayX = todayXOf(m);
31
+ return (_jsx("line", { "data-testid": "chart-today-line", x1: todayX, x2: todayX, y1: 0, y2: PLOT_HEIGHT, stroke: "var(--color-foreground)", strokeWidth: 1, strokeDasharray: "3 3", vectorEffect: "non-scaling-stroke" }));
32
+ }
33
+ export function renderPlotSvg(m) {
34
+ const { clipId } = m;
35
+ const hasToday = m.win.todayFraction !== undefined;
36
+ const ariaLabel = m.props.ariaLabel;
37
+ return (_jsxs("svg", { viewBox: `0 0 ${PLOT_WIDTH} ${PLOT_HEIGHT}`, preserveAspectRatio: "none", className: cn("block w-full", STACKED_AREA_PLOT_HEIGHT_CLASS), role: "img", "aria-label": ariaLabel, children: [_jsx("title", { children: ariaLabel }), renderDefs(m), renderGridLines(m), _jsx("g", { clipPath: `url(#${clipId}-past)`, children: renderBands(m, 0.55, 0.9) }), hasToday && (_jsx("g", { "data-testid": "chart-forecast-region", clipPath: `url(#${clipId}-future)`, children: renderBands(m, 0.25, 0.5) })), renderMarkerGuides(m), renderLinePaths(m), hasToday && renderTodayLine(m)] }));
38
+ }
@@ -0,0 +1,3 @@
1
+ import type { ReactNode } from "react";
2
+ import type { StackedAreaModel } from "./stacked-area-model.ts";
3
+ export declare function renderTooltipOverlay(m: StackedAreaModel): ReactNode;
@@ -0,0 +1,86 @@
1
+ import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { cn } from "../lib/cn.js";
3
+ import { chartColor, percent, valueAt } from "./chart-data.js";
4
+ import { bucketIndexAtFraction } from "./chart-window.js";
5
+ import { STACKED_AREA_PLOT_HEIGHT_CLASS } from "./stacked-area-geometry.js";
6
+ const TOOLTIP_WIDTH_REM = 14;
7
+ const TOOLTIP_OFFSET_PX = 8;
8
+ // A swipe over the plot (scrolling the page or the chart) must not open the card.
9
+ const TOOLTIP_TAP_MAX_MOVEMENT_PX = 8;
10
+ function buildTooltipRows(m, tooltipAtMs) {
11
+ const { tones, colors } = m.props;
12
+ const { series, lines } = m.win;
13
+ const { linePaths } = m.geometry;
14
+ return [
15
+ ...series.map((s, i) => ({
16
+ key: s.key,
17
+ label: s.label,
18
+ color: chartColor(s.key, i, tones, colors),
19
+ swatch: "area",
20
+ value: valueAt(s, tooltipAtMs),
21
+ })),
22
+ ...lines.flatMap((line, li) => {
23
+ const value = line.points.find((p) => p.atMs === tooltipAtMs)?.value;
24
+ return value === null || value === undefined
25
+ ? []
26
+ : [
27
+ {
28
+ key: line.key,
29
+ label: line.label,
30
+ color: linePaths[li]?.color ?? "var(--color-foreground)",
31
+ swatch: line.dashed === true ? "dashed-line" : "line",
32
+ value,
33
+ },
34
+ ];
35
+ }),
36
+ ];
37
+ }
38
+ function renderTooltipSwatch(row) {
39
+ if (row.swatch === "area") {
40
+ return (_jsx("span", { "aria-hidden": "true", className: "size-2 shrink-0 rounded-[2px]", style: { backgroundColor: row.color } }));
41
+ }
42
+ return (_jsx("span", { "aria-hidden": "true", className: "w-3 shrink-0 border-t-2", style: {
43
+ borderColor: row.color,
44
+ borderTopStyle: row.swatch === "dashed-line" ? "dashed" : "solid",
45
+ } }));
46
+ }
47
+ function renderTooltipCard(m, tooltipAtMs) {
48
+ const tooltipFraction = m.win.fractionOf(tooltipAtMs);
49
+ const tooltipOnLeft = tooltipFraction > 0.5;
50
+ const tooltipEdge = `${percent(tooltipFraction)} ${tooltipOnLeft ? "-" : "+"} ${TOOLTIP_OFFSET_PX}px`;
51
+ const tooltipLeft = tooltipOnLeft
52
+ ? `max(0px, calc(${tooltipEdge} - ${TOOLTIP_WIDTH_REM}rem))`
53
+ : `max(0px, min(calc(${tooltipEdge}), calc(100% - ${TOOLTIP_WIDTH_REM}rem)))`;
54
+ return (_jsxs(_Fragment, { children: [_jsx("div", { "aria-hidden": "true", className: "pointer-events-none absolute inset-y-0 w-px bg-foreground/40", style: { left: percent(tooltipFraction) } }), _jsxs("div", { "data-testid": "chart-tooltip", "data-side": tooltipOnLeft ? "left" : "right", className: "pointer-events-none absolute top-2 rounded-md border bg-popover p-2 text-xs text-popover-foreground shadow-sm", style: { left: tooltipLeft, width: `min(${TOOLTIP_WIDTH_REM}rem, 100%)` }, children: [_jsx("div", { className: "mb-1 font-medium", children: m.win.formatBucket(tooltipAtMs) }), buildTooltipRows(m, tooltipAtMs).map((row) => (_jsxs("div", { className: "flex items-center gap-1.5", children: [renderTooltipSwatch(row), _jsx("span", { className: "min-w-0 truncate text-muted-foreground", children: row.label }), _jsx("span", { className: "ml-auto tabular-nums", children: m.props.formatAxisValue(row.value) })] }, row.key)))] })] }));
55
+ }
56
+ function bucketAtMsUnderPointer(m, event) {
57
+ const { times, visibleWindow } = m.win;
58
+ const rect = event.currentTarget.getBoundingClientRect();
59
+ return (times[bucketIndexAtFraction(times, visibleWindow, rect.width === 0 ? 0 : (event.clientX - rect.left) / rect.width)] ?? null);
60
+ }
61
+ export function renderTooltipOverlay(m) {
62
+ const { tooltipAtMs, setSelectedTooltipAtMs, tooltipOverlayRef, touchStartRef } = m.tooltip;
63
+ return (_jsx("div", { ref: tooltipOverlayRef, "data-testid": "chart-tooltip-overlay", className: cn("absolute inset-x-0 top-0", STACKED_AREA_PLOT_HEIGHT_CLASS), onPointerMove: (event) => {
64
+ if (event.pointerType === "mouse")
65
+ setSelectedTooltipAtMs(bucketAtMsUnderPointer(m, event));
66
+ }, onPointerLeave: (event) => {
67
+ if (event.pointerType === "mouse")
68
+ setSelectedTooltipAtMs(null);
69
+ }, onPointerDown: (event) => {
70
+ if (event.pointerType === "mouse")
71
+ return;
72
+ touchStartRef.current = { x: event.clientX, y: event.clientY };
73
+ }, onPointerCancel: () => {
74
+ touchStartRef.current = null;
75
+ }, onPointerUp: (event) => {
76
+ const start = touchStartRef.current;
77
+ touchStartRef.current = null;
78
+ if (event.pointerType === "mouse" || start === null)
79
+ return;
80
+ const moved = Math.hypot(event.clientX - start.x, event.clientY - start.y);
81
+ if (moved >= TOOLTIP_TAP_MAX_MOVEMENT_PX)
82
+ return;
83
+ const next = bucketAtMsUnderPointer(m, event);
84
+ setSelectedTooltipAtMs((current) => (current === next ? null : next));
85
+ }, children: tooltipAtMs !== undefined && renderTooltipCard(m, tooltipAtMs) }));
86
+ }
@@ -0,0 +1,22 @@
1
+ import { type KeyboardEvent } from "react";
2
+ import { usePointerDrag } from "./use-pointer-drag.ts";
3
+ export type DrawerResizeConfig = {
4
+ readonly defaultWidthPx?: number;
5
+ readonly minWidthPx?: number;
6
+ readonly maxWidthPx?: number;
7
+ };
8
+ export type DrawerResizeState = {
9
+ readonly minWidthPx: number;
10
+ readonly resizedWidthPx: number;
11
+ readonly setResizedWidthPx: (update: number | ((current: number) => number)) => void;
12
+ readonly maximized: boolean;
13
+ readonly setMaximized: (update: boolean | ((current: boolean) => boolean)) => void;
14
+ readonly effectiveWidthPx: number;
15
+ readonly effectiveMaxWidthPx: () => number;
16
+ };
17
+ export declare function useDrawerResizeState(resize: DrawerResizeConfig | undefined): DrawerResizeState;
18
+ export type DrawerResizeHandle = {
19
+ readonly handleDrag: ReturnType<typeof usePointerDrag>;
20
+ readonly onHandleKeyDown: (event: KeyboardEvent<HTMLDivElement>) => void;
21
+ };
22
+ export declare function useDrawerResizeHandle(side: "left" | "right" | "top" | "bottom", resize: DrawerResizeState): DrawerResizeHandle;
@@ -0,0 +1,52 @@
1
+ import { useState } from "react";
2
+ import { clamp } from "../lib/clamp.js";
3
+ import { defaultWidthFromViewport, MAX_WIDTH_PX, MIN_WIDTH_PX } from "./drawer-sizing.js";
4
+ import { arrowKeyDelta, usePointerDrag } from "./use-pointer-drag.js";
5
+ export function useDrawerResizeState(resize) {
6
+ const minWidthPx = resize?.minWidthPx ?? MIN_WIDTH_PX;
7
+ const maxWidthPx = resize?.maxWidthPx ?? MAX_WIDTH_PX;
8
+ const effectiveMaxWidthPx = () => typeof window === "undefined"
9
+ ? maxWidthPx
10
+ : Math.min(maxWidthPx, Math.round(window.innerWidth * 0.9));
11
+ const [resizedWidthPx, setResizedWidthPx] = useState(() => clamp(resize?.defaultWidthPx ?? defaultWidthFromViewport(), minWidthPx, effectiveMaxWidthPx()));
12
+ const [maximized, setMaximized] = useState(false);
13
+ const effectiveWidthPx = maximized ? effectiveMaxWidthPx() : resizedWidthPx;
14
+ return {
15
+ minWidthPx,
16
+ resizedWidthPx,
17
+ setResizedWidthPx,
18
+ maximized,
19
+ setMaximized,
20
+ effectiveWidthPx,
21
+ effectiveMaxWidthPx,
22
+ };
23
+ }
24
+ export function useDrawerResizeHandle(side, resize) {
25
+ const { minWidthPx, setResizedWidthPx, maximized, setMaximized, effectiveWidthPx } = resize;
26
+ const { effectiveMaxWidthPx } = resize;
27
+ const handleDrag = usePointerDrag({
28
+ onStart: () => {
29
+ setMaximized(false);
30
+ return { startWidth: effectiveWidthPx };
31
+ },
32
+ onMove: ({ startWidth }, { dx }) => {
33
+ const signedDelta = side === "right" ? -dx : dx;
34
+ setResizedWidthPx(clamp(startWidth + signedDelta, minWidthPx, effectiveMaxWidthPx()));
35
+ },
36
+ });
37
+ const onHandleKeyDown = (event) => {
38
+ const keyDelta = arrowKeyDelta(event);
39
+ // skip: only horizontal arrow keys resize the drawer
40
+ if (keyDelta === undefined || keyDelta.dx === 0)
41
+ return;
42
+ event.preventDefault();
43
+ setMaximized(false);
44
+ const delta = side === "right" ? -keyDelta.dx : keyDelta.dx;
45
+ // Seed from the currently visible width, not the stale `resizedWidthPx`
46
+ // state — while maximized, it still holds the pre-maximize value, so a
47
+ // key press would otherwise jump the drawer back to that old size
48
+ // instead of resizing relative to what's on screen.
49
+ setResizedWidthPx((current) => clamp((maximized ? effectiveWidthPx : current) + delta, minWidthPx, effectiveMaxWidthPx()));
50
+ };
51
+ return { handleDrag, onHandleKeyDown };
52
+ }
@@ -0,0 +1,9 @@
1
+ export declare function useStackedAreaTooltip(times: readonly number[]): {
2
+ tooltipAtMs: number | undefined;
3
+ setSelectedTooltipAtMs: import("react").Dispatch<import("react").SetStateAction<number | null>>;
4
+ tooltipOverlayRef: import("react").RefObject<HTMLDivElement | null>;
5
+ touchStartRef: import("react").RefObject<{
6
+ readonly x: number;
7
+ readonly y: number;
8
+ } | null>;
9
+ };
@@ -0,0 +1,34 @@
1
+ import { useEffect, useRef, useState } from "react";
2
+ export function useStackedAreaTooltip(times) {
3
+ const [selectedTooltipAtMs, setSelectedTooltipAtMs] = useState(null);
4
+ // Stored as a time, not an index: a shifted window must not retarget the card to another bucket.
5
+ const tooltipAtMs = selectedTooltipAtMs !== null && times.includes(selectedTooltipAtMs)
6
+ ? selectedTooltipAtMs
7
+ : undefined;
8
+ const tooltipOverlayRef = useRef(null);
9
+ const touchStartRef = useRef(null);
10
+ const isTooltipOpen = tooltipAtMs !== undefined;
11
+ useEffect(() => {
12
+ // skip: no tooltip card open, nothing to dismiss
13
+ if (!isTooltipOpen)
14
+ return;
15
+ const closeOnEscape = (event) => {
16
+ if (event.key === "Escape")
17
+ setSelectedTooltipAtMs(null);
18
+ };
19
+ const closeOnOutsidePointerDown = (event) => {
20
+ const overlay = tooltipOverlayRef.current;
21
+ // skip: pointer landed inside the overlay, taps there select a bucket instead
22
+ if (overlay !== null && event.target instanceof Node && overlay.contains(event.target))
23
+ return;
24
+ setSelectedTooltipAtMs(null);
25
+ };
26
+ document.addEventListener("keydown", closeOnEscape);
27
+ document.addEventListener("pointerdown", closeOnOutsidePointerDown);
28
+ return () => {
29
+ document.removeEventListener("keydown", closeOnEscape);
30
+ document.removeEventListener("pointerdown", closeOnOutsidePointerDown);
31
+ };
32
+ }, [isTooltipOpen]);
33
+ return { tooltipAtMs, setSelectedTooltipAtMs, tooltipOverlayRef, touchStartRef };
34
+ }
@@ -0,0 +1,20 @@
1
+ import { type ChartWindow, type StackedAreaWindowSelection } from "./chart-window.ts";
2
+ import type { ResolvedStackedAreaProps } from "./stacked-area-props.ts";
3
+ export declare function useStackedAreaWindow(props: ResolvedStackedAreaProps): {
4
+ isControlled: boolean;
5
+ selection: StackedAreaWindowSelection | undefined;
6
+ changeSelection: (next: StackedAreaWindowSelection) => void;
7
+ allTimes: readonly number[];
8
+ visibleWindow: ChartWindow;
9
+ windowStartMs: number;
10
+ windowEndMs: number;
11
+ formatBucket: (atMs: number) => string;
12
+ tierTicks: (readonly number[])[];
13
+ formatMarkerTimeInWindow: ((atMs: number) => string) | undefined;
14
+ series: readonly import("./chart-data.ts").ChartSeries[];
15
+ lines: readonly import("./chart-data.ts").ChartLine[];
16
+ markers: readonly import("./chart-data.ts").ChartMarker[] | undefined;
17
+ times: readonly number[];
18
+ fractionOf: (atMs: number) => number;
19
+ todayFraction: number | undefined;
20
+ };
@@ -0,0 +1,76 @@
1
+ import { useMemo, useState } from "react";
2
+ import { bucketTimes } from "./chart-data.js";
3
+ import { initialWindowSelection, pointsWithin, resolveStackedAreaWindow, stackedAreaAxisTicks, stackedAreaDateFormatFor, } from "./chart-window.js";
4
+ import { AXIS_TIERS } from "./stacked-area-geometry.js";
5
+ function resolveVisibleWindow(props, selection, allTimes) {
6
+ const { ranges, brush, todayMs } = props;
7
+ const fullWindow = { startMs: props.windowStartMs, endMs: props.windowEndMs };
8
+ const isWindowing = ranges !== undefined || brush || selection !== undefined;
9
+ if (!isWindowing)
10
+ return fullWindow;
11
+ return resolveStackedAreaWindow({
12
+ selection,
13
+ ranges,
14
+ brush,
15
+ todayMs,
16
+ bucketTimes: allTimes,
17
+ fullWindow,
18
+ });
19
+ }
20
+ function restrictToWindow(props, visibleWindow) {
21
+ const { series: allSeries, lines: allLines, markers: allMarkers } = props;
22
+ const isNarrowed = visibleWindow.startMs !== props.windowStartMs || visibleWindow.endMs !== props.windowEndMs;
23
+ if (!isNarrowed)
24
+ return { series: allSeries, lines: allLines, markers: allMarkers };
25
+ return {
26
+ series: allSeries.map((s) => ({ ...s, points: pointsWithin(s.points, visibleWindow) })),
27
+ lines: allLines.map((line) => ({ ...line, points: pointsWithin(line.points, visibleWindow) })),
28
+ markers: allMarkers !== undefined ? pointsWithin(allMarkers, visibleWindow) : allMarkers,
29
+ };
30
+ }
31
+ export function useStackedAreaWindow(props) {
32
+ const { ranges, initialWindow, windowSelection, onWindowSelectionChange } = props;
33
+ const { dateFormat, timeZone, formatBucketLabel, formatMarkerTime, todayMs } = props;
34
+ const [internalSelection, setInternalSelection] = useState(initialWindowSelection(ranges, initialWindow));
35
+ const isControlled = onWindowSelectionChange !== undefined;
36
+ const selection = isControlled ? windowSelection : internalSelection;
37
+ const changeSelection = (next) => {
38
+ if (isControlled)
39
+ onWindowSelectionChange(next);
40
+ else
41
+ setInternalSelection(next);
42
+ };
43
+ const allTimes = bucketTimes([...props.series, ...props.lines]);
44
+ const visibleWindow = resolveVisibleWindow(props, selection, allTimes);
45
+ const windowStartMs = visibleWindow.startMs;
46
+ const windowEndMs = visibleWindow.endMs;
47
+ const effectiveDateFormat = dateFormat ?? stackedAreaDateFormatFor(visibleWindow);
48
+ const formatBucket = (atMs) => formatBucketLabel(atMs, effectiveDateFormat);
49
+ const tierTicks = useMemo(() => AXIS_TIERS.map((tier) => stackedAreaAxisTicks({ startMs: windowStartMs, endMs: windowEndMs }, tier.maxLabels, effectiveDateFormat, timeZone)), [windowStartMs, windowEndMs, effectiveDateFormat, timeZone]);
50
+ const formatMarkerTimeInWindow = formatMarkerTime === undefined
51
+ ? undefined
52
+ : (atMs) => formatMarkerTime(atMs, effectiveDateFormat);
53
+ const { series, lines, markers } = restrictToWindow(props, visibleWindow);
54
+ const times = bucketTimes([...series, ...lines]);
55
+ const span = Math.max(1, windowEndMs - windowStartMs);
56
+ const fractionOf = (atMs) => Math.max(0, Math.min(1, (atMs - windowStartMs) / span));
57
+ const todayFraction = todayMs !== undefined ? fractionOf(todayMs) : undefined;
58
+ return {
59
+ isControlled,
60
+ selection,
61
+ changeSelection,
62
+ allTimes,
63
+ visibleWindow,
64
+ windowStartMs,
65
+ windowEndMs,
66
+ formatBucket,
67
+ tierTicks,
68
+ formatMarkerTimeInWindow,
69
+ series,
70
+ lines,
71
+ markers,
72
+ times,
73
+ fractionOf,
74
+ todayFraction,
75
+ };
76
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cosmicdrift/kumiko-renderer",
3
- "version": "0.388.0",
3
+ "version": "0.390.0",
4
4
  "description": "React renderer for Kumiko screens. The root export holds the platform-agnostic logic (primitives contract, hooks, KumikoScreen, navigation and SSE abstractions); ./headless the DOM-free UI logic, ./live the HTTP dispatcher, ./web the browser bindings (HTML primitives, history navigation, EventSource live events, createKumikoApp).",
5
5
  "license": "MIT",
6
6
  "author": "Marc Frost <marc@cosmicdriftgamestudio.com>",
@@ -48,8 +48,8 @@
48
48
  }
49
49
  },
50
50
  "dependencies": {
51
- "@cosmicdrift/kumiko-framework": "0.388.0",
52
- "@cosmicdrift/kumiko-types": "0.388.0",
51
+ "@cosmicdrift/kumiko-framework": "0.390.0",
52
+ "@cosmicdrift/kumiko-types": "0.390.0",
53
53
  "@radix-ui/react-dialog": "^1.1.15",
54
54
  "@radix-ui/react-dropdown-menu": "^2.1.16",
55
55
  "@radix-ui/react-label": "^2.1.8",
@@ -59,10 +59,10 @@
59
59
  "@radix-ui/react-slot": "^1.2.4",
60
60
  "@radix-ui/react-toast": "^1.2.15",
61
61
  "@radix-ui/react-tooltip": "^1.2.8",
62
- "@tiptap/core": "^3.30.5",
63
- "@tiptap/pm": "^3.30.5",
64
- "@tiptap/react": "^3.30.5",
65
- "@tiptap/starter-kit": "^3.30.5",
62
+ "@tiptap/core": "^3.31.3",
63
+ "@tiptap/pm": "^3.31.3",
64
+ "@tiptap/react": "^3.31.3",
65
+ "@tiptap/starter-kit": "^3.31.3",
66
66
  "@types/qrcode": "^1.5.5",
67
67
  "class-variance-authority": "^0.7.1",
68
68
  "clsx": "^2.1.1",
@@ -92,8 +92,8 @@
92
92
  }
93
93
  },
94
94
  "devDependencies": {
95
- "@cosmicdrift/kumiko-locale-de": "0.388.0",
96
- "@cosmicdrift/kumiko-testing": "0.388.0",
95
+ "@cosmicdrift/kumiko-locale-de": "0.390.0",
96
+ "@cosmicdrift/kumiko-testing": "0.390.0",
97
97
  "@tailwindcss/cli": "^4.3.0",
98
98
  "@testing-library/react": "^16.3.2",
99
99
  "@testing-library/user-event": "^14.6.1",