@eifi1/ui-kit 0.48.1 → 0.49.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 (318) hide show
  1. package/README.md +28 -13
  2. package/dist/admin/admin-action-confirm.js +1 -1
  3. package/dist/admin/admin-action-confirm.js.map +1 -1
  4. package/dist/admin/invitations-panel.js +5 -1
  5. package/dist/admin/invitations-panel.js.map +1 -1
  6. package/dist/admin/user-roster.d.ts +11 -5
  7. package/dist/admin/user-roster.js +15 -4
  8. package/dist/admin/user-roster.js.map +1 -1
  9. package/dist/auth/accept-invitation.js +2 -2
  10. package/dist/auth/accept-invitation.js.map +1 -1
  11. package/dist/auth/not-found-page.js +1 -1
  12. package/dist/auth/not-found-page.js.map +1 -1
  13. package/dist/auth/reset-password-form.js +2 -2
  14. package/dist/auth/reset-password-form.js.map +1 -1
  15. package/dist/auth/verify-email.js +2 -2
  16. package/dist/auth/verify-email.js.map +1 -1
  17. package/dist/billing/plan-card.js +2 -2
  18. package/dist/billing/plan-card.js.map +1 -1
  19. package/dist/components/breadcrumbs.d.ts +19 -1
  20. package/dist/components/breadcrumbs.js +11 -4
  21. package/dist/components/breadcrumbs.js.map +1 -1
  22. package/dist/components/button-look.d.ts +37 -7
  23. package/dist/components/button-look.js +33 -6
  24. package/dist/components/button-look.js.map +1 -1
  25. package/dist/components/button.d.ts +4 -1
  26. package/dist/components/button.js +2 -2
  27. package/dist/components/button.js.map +1 -1
  28. package/dist/components/calendar-heatmap.js +2 -1
  29. package/dist/components/calendar-heatmap.js.map +1 -1
  30. package/dist/components/chart-edge.d.ts +10 -0
  31. package/dist/components/chart-edge.js +57 -0
  32. package/dist/components/chart-edge.js.map +1 -0
  33. package/dist/components/chart.js +2 -1
  34. package/dist/components/chart.js.map +1 -1
  35. package/dist/components/chip.d.ts +8 -1
  36. package/dist/components/chip.js +4 -1
  37. package/dist/components/chip.js.map +1 -1
  38. package/dist/components/confirm-context.d.ts +5 -0
  39. package/dist/components/confirm-context.js +7 -0
  40. package/dist/components/confirm-context.js.map +1 -0
  41. package/dist/components/confirm-dialog.js +3 -3
  42. package/dist/components/confirm-dialog.js.map +1 -1
  43. package/dist/components/danger-confirm.d.ts +2 -2
  44. package/dist/components/danger-confirm.js +1 -1
  45. package/dist/components/danger-confirm.js.map +1 -1
  46. package/dist/components/data-table-fit.d.ts +8 -1
  47. package/dist/components/data-table-fit.js +28 -4
  48. package/dist/components/data-table-fit.js.map +1 -1
  49. package/dist/components/data-table-groups.d.ts +3 -1
  50. package/dist/components/data-table-groups.js +11 -5
  51. package/dist/components/data-table-groups.js.map +1 -1
  52. package/dist/components/data-table-head.d.ts +8 -1
  53. package/dist/components/data-table-head.js +11 -3
  54. package/dist/components/data-table-head.js.map +1 -1
  55. package/dist/components/data-table-leading-row.d.ts +3 -1
  56. package/dist/components/data-table-leading-row.js +2 -1
  57. package/dist/components/data-table-leading-row.js.map +1 -1
  58. package/dist/components/data-table-mobile.d.ts +48 -2
  59. package/dist/components/data-table-mobile.js +87 -10
  60. package/dist/components/data-table-mobile.js.map +1 -1
  61. package/dist/components/data-table-row-actions.d.ts +2 -1
  62. package/dist/components/data-table-row-actions.js +9 -2
  63. package/dist/components/data-table-row-actions.js.map +1 -1
  64. package/dist/components/data-table-row.d.ts +84 -0
  65. package/dist/components/data-table-row.js +261 -0
  66. package/dist/components/data-table-row.js.map +1 -0
  67. package/dist/components/data-table-selection.d.ts +3 -0
  68. package/dist/components/data-table-selection.js +12 -1
  69. package/dist/components/data-table-selection.js.map +1 -1
  70. package/dist/components/data-table-virtual.d.ts +98 -0
  71. package/dist/components/data-table-virtual.js +369 -0
  72. package/dist/components/data-table-virtual.js.map +1 -0
  73. package/dist/components/data-table.d.ts +74 -2
  74. package/dist/components/data-table.js +241 -221
  75. package/dist/components/data-table.js.map +1 -1
  76. package/dist/components/error-boundary.js +2 -2
  77. package/dist/components/error-boundary.js.map +1 -1
  78. package/dist/components/floating-panel.d.ts +36 -13
  79. package/dist/components/floating-panel.js +27 -7
  80. package/dist/components/floating-panel.js.map +1 -1
  81. package/dist/components/form-actions.d.ts +1 -1
  82. package/dist/components/form-actions.js +1 -1
  83. package/dist/components/form-actions.js.map +1 -1
  84. package/dist/components/icon-button.d.ts +11 -0
  85. package/dist/components/icon-button.js +7 -5
  86. package/dist/components/icon-button.js.map +1 -1
  87. package/dist/components/nav-pills.d.ts +8 -1
  88. package/dist/components/nav-pills.js +61 -38
  89. package/dist/components/nav-pills.js.map +1 -1
  90. package/dist/components/page-header-context.d.ts +8 -0
  91. package/dist/components/page-header-context.js +7 -0
  92. package/dist/components/page-header-context.js.map +1 -0
  93. package/dist/components/page-header.d.ts +14 -2
  94. package/dist/components/page-header.js +10 -1
  95. package/dist/components/page-header.js.map +1 -1
  96. package/dist/components/pie-chart.js +2 -1
  97. package/dist/components/pie-chart.js.map +1 -1
  98. package/dist/components/progress-bar.js +5 -4
  99. package/dist/components/progress-bar.js.map +1 -1
  100. package/dist/components/reauth-dialog.js +1 -1
  101. package/dist/components/reauth-dialog.js.map +1 -1
  102. package/dist/components/row-actions-labels.js +3 -1
  103. package/dist/components/row-actions-labels.js.map +1 -1
  104. package/dist/components/row-actions.d.ts +43 -9
  105. package/dist/components/row-actions.js +78 -15
  106. package/dist/components/row-actions.js.map +1 -1
  107. package/dist/components/section-nav.d.ts +37 -0
  108. package/dist/components/section-nav.js +33 -0
  109. package/dist/components/section-nav.js.map +1 -0
  110. package/dist/components/series-chart.js +8 -3
  111. package/dist/components/series-chart.js.map +1 -1
  112. package/dist/components/share-card.js +1 -1
  113. package/dist/components/share-card.js.map +1 -1
  114. package/dist/components/signature-pad.js +1 -1
  115. package/dist/components/signature-pad.js.map +1 -1
  116. package/dist/components/sparkline.js +2 -1
  117. package/dist/components/sparkline.js.map +1 -1
  118. package/dist/components/stat-tile.js +14 -7
  119. package/dist/components/stat-tile.js.map +1 -1
  120. package/dist/components/status-dot.d.ts +5 -1
  121. package/dist/components/status-dot.js +4 -1
  122. package/dist/components/status-dot.js.map +1 -1
  123. package/dist/components/tooltip.js +1 -1
  124. package/dist/components/tooltip.js.map +1 -1
  125. package/dist/components/translation-review-editor.js +2 -2
  126. package/dist/components/translation-review-editor.js.map +1 -1
  127. package/dist/components/translation-review.js +2 -2
  128. package/dist/components/translation-review.js.map +1 -1
  129. package/dist/components/treemap.d.ts +5 -2
  130. package/dist/components/treemap.js +26 -6
  131. package/dist/components/treemap.js.map +1 -1
  132. package/dist/components/use-table-state.js +37 -6
  133. package/dist/components/use-table-state.js.map +1 -1
  134. package/dist/data-table.d.ts +1 -0
  135. package/dist/data-table.js.map +1 -1
  136. package/dist/demo/demo-banner.js +1 -1
  137. package/dist/demo/demo-banner.js.map +1 -1
  138. package/dist/demo/demo-ended.js +1 -1
  139. package/dist/demo/demo-ended.js.map +1 -1
  140. package/dist/demo/demo-start.js +1 -1
  141. package/dist/demo/demo-start.js.map +1 -1
  142. package/dist/eslint/index.js +1 -1
  143. package/dist/feedback/feedback-inbox.js +1 -1
  144. package/dist/feedback/feedback-inbox.js.map +1 -1
  145. package/dist/hooks/use-anchored-panel.js +2 -1
  146. package/dist/hooks/use-anchored-panel.js.map +1 -1
  147. package/dist/hooks/use-dismiss.d.ts +9 -1
  148. package/dist/hooks/use-dismiss.js +25 -5
  149. package/dist/hooks/use-dismiss.js.map +1 -1
  150. package/dist/hooks/use-document-title.d.ts +14 -0
  151. package/dist/hooks/use-document-title.js +22 -0
  152. package/dist/hooks/use-document-title.js.map +1 -0
  153. package/dist/i18n/german.js +6 -2
  154. package/dist/i18n/german.js.map +1 -1
  155. package/dist/i18n/kit-labels.d.ts +31 -1
  156. package/dist/i18n/kit-labels.js +43 -2
  157. package/dist/i18n/kit-labels.js.map +1 -1
  158. package/dist/i18n/locales/en.js +6 -2
  159. package/dist/i18n/locales/en.js.map +2 -2
  160. package/dist/i18n/locales/es.js +6 -2
  161. package/dist/i18n/locales/es.js.map +1 -1
  162. package/dist/i18n/locales/fr.js +6 -2
  163. package/dist/i18n/locales/fr.js.map +1 -1
  164. package/dist/i18n/locales/hu.js +6 -2
  165. package/dist/i18n/locales/hu.js.map +1 -1
  166. package/dist/i18n/locales/it.js +6 -2
  167. package/dist/i18n/locales/it.js.map +1 -1
  168. package/dist/i18n/locales/zh.js +6 -2
  169. package/dist/i18n/locales/zh.js.map +1 -1
  170. package/dist/i18n/review.js +2 -0
  171. package/dist/i18n/review.js.map +1 -1
  172. package/dist/i18n/shared-labels.js +1 -0
  173. package/dist/i18n/shared-labels.js.map +1 -1
  174. package/dist/index.d.ts +8 -1
  175. package/dist/index.js +11 -0
  176. package/dist/index.js.map +1 -1
  177. package/dist/landing/landing-actions.js +2 -2
  178. package/dist/landing/landing-actions.js.map +1 -1
  179. package/dist/landing/public-header.js +2 -2
  180. package/dist/landing/public-header.js.map +1 -1
  181. package/dist/lib/clipboard-minus.d.ts +27 -0
  182. package/dist/lib/clipboard-minus.js +63 -0
  183. package/dist/lib/clipboard-minus.js.map +1 -0
  184. package/dist/lib/document-title.d.ts +17 -0
  185. package/dist/lib/document-title.js +29 -0
  186. package/dist/lib/document-title.js.map +1 -0
  187. package/dist/lib/float-clearance.js +8 -2
  188. package/dist/lib/float-clearance.js.map +1 -1
  189. package/dist/lib/format.d.ts +35 -10
  190. package/dist/lib/format.js +55 -11
  191. package/dist/lib/format.js.map +1 -1
  192. package/dist/lib/minus.d.ts +22 -0
  193. package/dist/lib/minus.js +14 -0
  194. package/dist/lib/minus.js.map +1 -0
  195. package/dist/lib/status-tone.d.ts +30 -0
  196. package/dist/lib/status-tone.js +17 -0
  197. package/dist/lib/status-tone.js.map +1 -0
  198. package/dist/settings/settings-layout.js +3 -2
  199. package/dist/settings/settings-layout.js.map +1 -1
  200. package/dist/shell/account-menu-language.d.ts +1 -1
  201. package/dist/shell/account-menu-language.js +9 -1
  202. package/dist/shell/account-menu-language.js.map +1 -1
  203. package/dist/shell/app-shell-labels.js +2 -1
  204. package/dist/shell/app-shell-labels.js.map +1 -1
  205. package/dist/shell/app-shell.d.ts +58 -16
  206. package/dist/shell/app-shell.js +158 -44
  207. package/dist/shell/app-shell.js.map +1 -1
  208. package/dist/shell/language-code.d.ts +3 -0
  209. package/dist/shell/language-code.js +8 -0
  210. package/dist/shell/language-code.js.map +1 -0
  211. package/dist/shell/topbar-controls.d.ts +14 -6
  212. package/dist/shell/topbar-controls.js +14 -17
  213. package/dist/shell/topbar-controls.js.map +1 -1
  214. package/dist/testing/css-variables.generated.d.ts +1 -1
  215. package/dist/testing/css-variables.generated.js +1 -1
  216. package/dist/testing/css-variables.generated.js.map +1 -1
  217. package/dist/theme/chart-palette.d.ts +82 -15
  218. package/dist/theme/chart-palette.js +51 -24
  219. package/dist/theme/chart-palette.js.map +1 -1
  220. package/dist/theme/palette-presets.d.ts +9 -0
  221. package/dist/theme/palette-presets.js +77 -62
  222. package/dist/theme/palette-presets.js.map +1 -1
  223. package/dist/tour/tour.js +1 -1
  224. package/dist/tour/tour.js.map +1 -1
  225. package/dist/wizard/stepper-nav.d.ts +1 -1
  226. package/dist/wizard/stepper-nav.js +3 -3
  227. package/dist/wizard/stepper-nav.js.map +1 -1
  228. package/package.json +1 -1
  229. package/src/admin/admin-action-confirm.tsx +1 -1
  230. package/src/admin/invitations-panel.tsx +7 -3
  231. package/src/admin/user-roster.tsx +23 -6
  232. package/src/auth/accept-invitation.tsx +3 -3
  233. package/src/auth/not-found-page.tsx +1 -1
  234. package/src/auth/reset-password-form.tsx +3 -3
  235. package/src/auth/verify-email.tsx +3 -3
  236. package/src/billing/plan-card.tsx +2 -2
  237. package/src/components/breadcrumbs.tsx +32 -4
  238. package/src/components/button-look.ts +86 -15
  239. package/src/components/button.tsx +6 -3
  240. package/src/components/calendar-heatmap.tsx +2 -1
  241. package/src/components/chart-edge.ts +73 -0
  242. package/src/components/chart.tsx +3 -1
  243. package/src/components/chip.tsx +10 -1
  244. package/src/components/confirm-context.ts +7 -0
  245. package/src/components/confirm-dialog.tsx +3 -3
  246. package/src/components/danger-confirm.tsx +3 -3
  247. package/src/components/data-table-fit.ts +58 -8
  248. package/src/components/data-table-groups.tsx +15 -4
  249. package/src/components/data-table-head.tsx +25 -2
  250. package/src/components/data-table-leading-row.tsx +4 -1
  251. package/src/components/data-table-mobile.tsx +162 -11
  252. package/src/components/data-table-row-actions.tsx +12 -3
  253. package/src/components/data-table-row.tsx +485 -0
  254. package/src/components/data-table-selection.ts +17 -1
  255. package/src/components/data-table-virtual.ts +547 -0
  256. package/src/components/data-table.tsx +367 -326
  257. package/src/components/error-boundary.tsx +2 -2
  258. package/src/components/floating-panel.tsx +67 -16
  259. package/src/components/form-actions.tsx +2 -2
  260. package/src/components/icon-button.tsx +21 -8
  261. package/src/components/nav-pills.tsx +29 -2
  262. package/src/components/page-header-context.ts +8 -0
  263. package/src/components/page-header.tsx +34 -2
  264. package/src/components/pie-chart.tsx +2 -1
  265. package/src/components/progress-bar.tsx +6 -3
  266. package/src/components/reauth-dialog.tsx +1 -1
  267. package/src/components/row-actions-labels.ts +2 -0
  268. package/src/components/row-actions.tsx +154 -33
  269. package/src/components/section-nav.tsx +65 -0
  270. package/src/components/series-chart.tsx +12 -3
  271. package/src/components/share-card.tsx +1 -1
  272. package/src/components/signature-pad.tsx +1 -1
  273. package/src/components/sparkline.tsx +2 -1
  274. package/src/components/stat-tile.tsx +19 -7
  275. package/src/components/status-dot.tsx +7 -1
  276. package/src/components/tooltip.tsx +3 -1
  277. package/src/components/translation-review-editor.tsx +2 -2
  278. package/src/components/translation-review.tsx +2 -2
  279. package/src/components/treemap.tsx +34 -8
  280. package/src/components/use-table-state.ts +63 -8
  281. package/src/data-table.ts +5 -3
  282. package/src/demo/demo-banner.tsx +1 -1
  283. package/src/demo/demo-ended.tsx +1 -1
  284. package/src/demo/demo-start.tsx +1 -1
  285. package/src/feedback/feedback-inbox.tsx +1 -1
  286. package/src/hooks/use-anchored-panel.ts +9 -1
  287. package/src/hooks/use-dismiss.ts +43 -4
  288. package/src/hooks/use-document-title.ts +31 -0
  289. package/src/i18n/german.ts +4 -0
  290. package/src/i18n/kit-labels.tsx +76 -1
  291. package/src/i18n/locales/es.ts +4 -0
  292. package/src/i18n/locales/fr.ts +4 -0
  293. package/src/i18n/locales/hu.ts +4 -0
  294. package/src/i18n/locales/it.ts +4 -0
  295. package/src/i18n/locales/zh.ts +4 -0
  296. package/src/i18n/review.ts +2 -0
  297. package/src/i18n/shared-labels.ts +1 -0
  298. package/src/index.ts +13 -0
  299. package/src/landing/landing-actions.tsx +2 -2
  300. package/src/landing/public-header.tsx +2 -2
  301. package/src/lib/clipboard-minus.ts +94 -0
  302. package/src/lib/document-title.ts +41 -0
  303. package/src/lib/float-clearance.ts +19 -3
  304. package/src/lib/format.ts +115 -30
  305. package/src/lib/minus.ts +40 -0
  306. package/src/lib/status-tone.ts +43 -0
  307. package/src/settings/settings-layout.tsx +3 -2
  308. package/src/shell/account-menu-language.tsx +14 -2
  309. package/src/shell/app-shell-labels.ts +1 -0
  310. package/src/shell/app-shell.tsx +298 -69
  311. package/src/shell/language-code.ts +5 -0
  312. package/src/shell/topbar-controls.tsx +35 -15
  313. package/src/testing/css-variables.generated.ts +6 -6
  314. package/src/theme/chart-palette.ts +102 -20
  315. package/src/theme/palette-presets.ts +121 -84
  316. package/src/tour/tour.tsx +1 -1
  317. package/src/wizard/stepper-nav.tsx +4 -4
  318. package/tokens.css +28 -18
@@ -33,13 +33,17 @@ function DataTableHead({
33
33
  clearFilter,
34
34
  startResize,
35
35
  moveResize,
36
- finishResize
36
+ finishResize,
37
+ ariaRowIndex,
38
+ lockedWidths,
39
+ selectionLockKey
37
40
  }) {
38
- return /* @__PURE__ */ jsx("thead", { className: "bg-surface-2 text-secondary", children: /* @__PURE__ */ jsxs("tr", { className: "text-start", children: [
41
+ return /* @__PURE__ */ jsx("thead", { className: "bg-surface-2 text-secondary", children: /* @__PURE__ */ jsxs("tr", { className: "text-start", "aria-rowindex": ariaRowIndex, children: [
39
42
  selection && /* @__PURE__ */ jsx(
40
43
  "th",
41
44
  {
42
45
  scope: "col",
46
+ style: selectionLockKey && lockedWidths?.[selectionLockKey] !== void 0 ? { minWidth: lockedWidths[selectionLockKey] } : void 0,
43
47
  className: cn(
44
48
  "sticky top-0 z-10 bg-surface-2 align-middle backdrop-blur-sm",
45
49
  compact ? "w-8 px-2 py-1" : "w-10 px-3 py-2"
@@ -83,7 +87,11 @@ function DataTableHead({
83
87
  },
84
88
  scope: "col",
85
89
  "aria-sort": sortable ? sortIdx === 0 ? sorts[0].dir === "asc" ? "ascending" : "descending" : "none" : void 0,
86
- style: ownHead.style || pinnedHead ? { ...ownHead.style, ...pinnedHead } : void 0,
90
+ style: ownHead.style || pinnedHead || lockedWidths?.[col.key] !== void 0 ? {
91
+ ...ownHead.style,
92
+ ...lockedWidths?.[col.key] !== void 0 ? { minWidth: lockedWidths[col.key] } : null,
93
+ ...pinnedHead
94
+ } : void 0,
87
95
  className: cn(
88
96
  "relative font-medium align-middle whitespace-nowrap",
89
97
  compact ? "px-2 py-1" : "px-3 py-2",
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/components/data-table-head.tsx"],"sourcesContent":["import type { Dispatch, SetStateAction } from \"react\";\nimport { ArrowDown, ArrowUp, ArrowUpDown, Filter } from \"lucide-react\";\nimport { cn } from \"../lib/cn\";\nimport { defaultFilterState, isFilterActive, resolveFilter } from \"./data-table-filters\";\nimport type { FilterValue } from \"./data-table-filters\";\nimport type { SortState } from \"./data-table-sort\";\nimport { FilterPopover } from \"./data-table-filter-popover\";\nimport { Popover } from \"./popover\";\nimport { Tooltip } from \"./tooltip\";\nimport type { DataTableLabels } from \"./data-table-labels\";\nimport { isTableOwnColumn } from \"./data-table-row-actions\";\nimport { columnLabel, logicalAlign, splitCellProps } from \"./data-table-shared\";\nimport { GROW_HEAD_CLASS } from \"./data-table-fit\";\nimport type { DataTableColumn, DataTableProps, FilterState } from \"./data-table\";\n\n// DataTable's `<thead>` on a wide screen: the select-all box, and per column the sort\n// button, the filter popover and the resize handle. Moved out of data-table.tsx whole\n// (refactor audit 2026-10, MOD-3 step 5); the state it shows and changes stays the\n// table's.\n\n/** Whether a column is end-aligned — the header then puts its filter button first. */\nconst END_ALIGN = /(^|\\s)text-end(?=\\s|$)/;\n\n/** The wide-screen table's header row. */\nexport function DataTableHead<T>({\n visibleColumns,\n selection,\n compact,\n labels,\n locale,\n isServer,\n hasSortCallback,\n hasFilterCallback,\n sorts,\n filters,\n selectOptionsByKey,\n multiSort,\n resizable,\n widths,\n setWidths,\n headRefs,\n toggleSort,\n setFilterValue,\n clearFilter,\n startResize,\n moveResize,\n finishResize,\n}: {\n visibleColumns: DataTableColumn<T>[];\n selection: DataTableProps<T>[\"selection\"];\n compact: boolean;\n labels: DataTableLabels;\n locale?: string;\n isServer: boolean;\n /** The caller owns the sort (`onSortsChange`), so a server table can offer it. */\n hasSortCallback: boolean;\n /** The caller owns the filters (`onFiltersChange`), likewise. */\n hasFilterCallback: boolean;\n sorts: SortState[];\n filters: FilterState;\n selectOptionsByKey: Record<string, { value: string; label: string }[]>;\n multiSort: boolean;\n resizable: boolean;\n widths: Record<string, number>;\n setWidths: Dispatch<SetStateAction<Record<string, number>>>;\n /** Filled here, one `<th>` per column — what a resize and \"Auto-size\" measure. */\n headRefs: Map<string, HTMLTableCellElement>;\n toggleSort: (key: string, shiftKey: boolean) => void;\n setFilterValue: (key: string, value: FilterValue) => void;\n clearFilter: (key: string) => void;\n startResize: (e: React.PointerEvent<HTMLElement>, key: string) => void;\n moveResize: (e: React.PointerEvent<HTMLElement>) => void;\n finishResize: (e: React.PointerEvent<HTMLElement>, commit: boolean) => void;\n}) {\n return (\n <thead className=\"bg-surface-2 text-secondary\">\n <tr className=\"text-start\">\n {selection && (\n <th\n scope=\"col\"\n className={cn(\n \"sticky top-0 z-10 bg-surface-2 align-middle backdrop-blur-sm\",\n compact ? \"w-8 px-2 py-1\" : \"w-10 px-3 py-2\",\n )}\n >\n <input\n type=\"checkbox\"\n className={cn(\"align-middle accent-indigo-600\", compact ? \"size-3.5\" : \"size-4\")}\n checked={selection.allSelected}\n ref={(el) => {\n if (el) el.indeterminate = selection.someSelected && !selection.allSelected;\n }}\n onChange={(e) => selection.onToggleAll(e.target.checked)}\n aria-label={labels.selectAllRows}\n />\n </th>\n )}\n {visibleColumns.map((col) => {\n // In server mode the utilities stay hidden unless the caller\n // takes them over and feeds them back into the server query.\n const sortable = (!isServer || hasSortCallback) && !!col.sortBy;\n const filter = !isServer || hasFilterCallback ? resolveFilter(col) : null;\n const filterState = filters[col.key] ?? (filter ? defaultFilterState(filter) : undefined);\n const sortIdx = sorts.findIndex((s) => s.key === col.key);\n const active = sortIdx !== -1;\n const Icon = active ? (sorts[sortIdx].dir === \"asc\" ? ArrowUp : ArrowDown) : ArrowUpDown;\n const filterActive = filterState && isFilterActive(filterState);\n // Logical, and a legacy `text-right` still counts — see `logicalAlign`.\n const headClass = logicalAlign(col.headClassName);\n const isEndAligned = !!headClass && END_ALIGN.test(headClass);\n const width = widths[col.key];\n const ownHead = splitCellProps(col.headProps);\n const pinnedHead = width ? { width, minWidth: width, maxWidth: width } : undefined;\n return (\n <th\n {...ownHead.attrs}\n key={col.key}\n ref={(el) => {\n if (el) headRefs.set(col.key, el);\n else headRefs.delete(col.key);\n }}\n scope=\"col\"\n // Only a header the user can actually sort says anything: an\n // `aria-sort` on a plain column is a control that is not there,\n // and a persisted or URL sort can name a column with no `sortBy`\n // (the sort chain drops it silently) — which would otherwise\n // claim a sort the table is not applying.\n //\n // \"none\" on the rest is the half that was missing. Without it a\n // sortable header is indistinguishable from a plain one until the\n // user presses it. Secondary criteria read \"none\" too: ARIA allows\n // exactly one sorted column, and the priority is carried by the\n // badge and by what the header button announces on press.\n aria-sort={\n sortable\n ? sortIdx === 0\n ? sorts[0].dir === \"asc\"\n ? \"ascending\"\n : \"descending\"\n : \"none\"\n : undefined\n }\n style={ownHead.style || pinnedHead ? { ...ownHead.style, ...pinnedHead } : undefined}\n className={cn(\n \"relative font-medium align-middle whitespace-nowrap\",\n compact ? \"px-2 py-1\" : \"px-3 py-2\",\n // Keep the header visible while the user scrolls the page.\n // Each <th> carries its own bg so the row doesn't render\n // transparent over the data rows underneath.\n \"sticky top-0 z-10 bg-surface-2 backdrop-blur-sm\",\n col.grow && GROW_HEAD_CLASS,\n logicalAlign(ownHead.className),\n headClass,\n )}\n >\n <div\n className={cn(\n \"flex items-center gap-1\",\n isEndAligned && \"flex-row-reverse\",\n )}\n >\n <Tooltip label={sortable && multiSort ? labels.sortHint : undefined} portal>\n <button\n type=\"button\"\n onClick={(e) => sortable && toggleSort(col.key, e.shiftKey)}\n disabled={!sortable}\n className={cn(\n \"flex items-center gap-1 text-start\",\n sortable && \"hover:text-primary\",\n )}\n >\n <span>{col.header}</span>\n {sortable && (\n <Icon className={cn(\"size-3\", active ? \"opacity-100\" : \"opacity-40\")} />\n )}\n {/* Priority badge, only meaningful with 2+ sort keys */}\n {active && sorts.length > 1 && (\n <span className=\"text-micro font-semibold leading-none text-brand\">\n {sortIdx + 1}\n </span>\n )}\n </button>\n </Tooltip>\n {filter && (\n <Popover\n // 420 px at Normal, in rem so the calendar grows with its\n // text (§3.2); Popover caps it at the viewport.\n width={filter.type === \"date\" ? \"26.25rem\" : undefined}\n // The panel is THIS column's filter, and says so. Unnamed it was\n // announced as the kit-wide fallback, \"Popover\"; then as \"Filter\"\n // in every column alike (round 0.34 Wave 2, BUG-7c).\n labels={{ panel: labels.filterColumn(columnLabel(col, labels), false) }}\n trigger={({ open, toggle, ref }) => (\n <button\n type=\"button\"\n ref={ref}\n onClick={toggle}\n // Named for its column, and for being filtered: the tint\n // alone said that to nobody who cannot see it (BUG-7c).\n aria-label={labels.filterColumn(columnLabel(col, labels), Boolean(filterActive))}\n aria-haspopup=\"dialog\"\n aria-expanded={open}\n className={cn(\n \"rounded p-1 transition-colors\",\n filterActive\n ? \"bg-brand-soft text-brand-muted\"\n : \"text-placeholder hover:text-secondary hover:bg-hover\",\n )}\n >\n <Filter className=\"size-3\" />\n </button>\n )}\n >\n {() => (\n <FilterPopover\n column={col}\n state={filterState ?? defaultFilterState(filter)}\n onChange={(next) => setFilterValue(col.key, next)}\n onClear={() => clearFilter(col.key)}\n selectOptions={selectOptionsByKey[col.key] ?? []}\n labels={labels}\n locale={locale}\n />\n )}\n </Popover>\n )}\n </div>\n {/* A `grow` column takes the rest: no width of its own to drag (0.48, KS N2). */}\n {resizable && !isTableOwnColumn(col.key) && !col.grow && (\n /* eslint-disable-next-line jsx-a11y/no-noninteractive-element-interactions -- a\n pointer-only drag handle for a visual preference (column width); it is\n deliberately not a tab stop, and no content or action depends on it. */\n <span\n role=\"separator\"\n aria-orientation=\"vertical\"\n onPointerDown={(e) => startResize(e, col.key)}\n onPointerMove={moveResize}\n onPointerUp={(e) => finishResize(e, true)}\n onPointerCancel={(e) => finishResize(e, false)}\n onDoubleClick={(e) => {\n e.preventDefault();\n setWidths((w) => {\n const next = { ...w };\n delete next[col.key];\n return next;\n });\n }}\n // `touch-none`: without it the browser claims a finger drag for\n // scrolling before the first `pointermove` ever arrives.\n // `end-0` and a translate that flips with it: the handle sits on the column's\n // trailing edge, which is its left one in a right-to-left table.\n className=\"absolute end-0 top-0 z-10 h-full w-1.5 -translate-x-1/2 rtl:translate-x-1/2 cursor-col-resize select-none touch-none hover:bg-brand-soft-hover\"\n />\n )}\n </th>\n );\n })}\n </tr>\n </thead>\n );\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../src/components/data-table-head.tsx"],"sourcesContent":["import type { Dispatch, SetStateAction } from \"react\";\nimport { ArrowDown, ArrowUp, ArrowUpDown, Filter } from \"lucide-react\";\nimport { cn } from \"../lib/cn\";\nimport { defaultFilterState, isFilterActive, resolveFilter } from \"./data-table-filters\";\nimport type { FilterValue } from \"./data-table-filters\";\nimport type { SortState } from \"./data-table-sort\";\nimport { FilterPopover } from \"./data-table-filter-popover\";\nimport { Popover } from \"./popover\";\nimport { Tooltip } from \"./tooltip\";\nimport type { DataTableLabels } from \"./data-table-labels\";\nimport { isTableOwnColumn } from \"./data-table-row-actions\";\nimport { columnLabel, logicalAlign, splitCellProps } from \"./data-table-shared\";\nimport { GROW_HEAD_CLASS } from \"./data-table-fit\";\nimport type { DataTableColumn, DataTableProps, FilterState } from \"./data-table\";\n\n// DataTable's `<thead>` on a wide screen: the select-all box, and per column the sort\n// button, the filter popover and the resize handle. Moved out of data-table.tsx whole\n// (refactor audit 2026-10, MOD-3 step 5); the state it shows and changes stays the\n// table's.\n\n/** Whether a column is end-aligned — the header then puts its filter button first. */\nconst END_ALIGN = /(^|\\s)text-end(?=\\s|$)/;\n\n/** The wide-screen table's header row. */\nexport function DataTableHead<T>({\n visibleColumns,\n selection,\n compact,\n labels,\n locale,\n isServer,\n hasSortCallback,\n hasFilterCallback,\n sorts,\n filters,\n selectOptionsByKey,\n multiSort,\n resizable,\n widths,\n setWidths,\n headRefs,\n toggleSort,\n setFilterValue,\n clearFilter,\n startResize,\n moveResize,\n finishResize,\n ariaRowIndex,\n lockedWidths,\n selectionLockKey,\n}: {\n visibleColumns: DataTableColumn<T>[];\n selection: DataTableProps<T>[\"selection\"];\n compact: boolean;\n labels: DataTableLabels;\n locale?: string;\n isServer: boolean;\n /** The caller owns the sort (`onSortsChange`), so a server table can offer it. */\n hasSortCallback: boolean;\n /** The caller owns the filters (`onFiltersChange`), likewise. */\n hasFilterCallback: boolean;\n sorts: SortState[];\n filters: FilterState;\n selectOptionsByKey: Record<string, { value: string; label: string }[]>;\n multiSort: boolean;\n resizable: boolean;\n widths: Record<string, number>;\n setWidths: Dispatch<SetStateAction<Record<string, number>>>;\n /** Filled here, one `<th>` per column — what a resize and \"Auto-size\" measure. */\n headRefs: Map<string, HTMLTableCellElement>;\n toggleSort: (key: string, shiftKey: boolean) => void;\n setFilterValue: (key: string, value: FilterValue) => void;\n clearFilter: (key: string) => void;\n startResize: (e: React.PointerEvent<HTMLElement>, key: string) => void;\n moveResize: (e: React.PointerEvent<HTMLElement>) => void;\n finishResize: (e: React.PointerEvent<HTMLElement>, commit: boolean) => void;\n /** Under `virtualize` (0.48.2): 1. */\n ariaRowIndex?: number;\n /** Under `virtualize` (0.48.2): each column's locked width, as the header cell's\n * `min-width` — a `<col>` width alone gave way to a `grow` column's 100%. */\n lockedWidths?: Record<string, number> | null;\n /** The selection column's key in `lockedWidths`. */\n selectionLockKey?: string;\n}) {\n return (\n <thead className=\"bg-surface-2 text-secondary\">\n <tr className=\"text-start\" aria-rowindex={ariaRowIndex}>\n {selection && (\n <th\n scope=\"col\"\n style={\n selectionLockKey && lockedWidths?.[selectionLockKey] !== undefined\n ? { minWidth: lockedWidths[selectionLockKey] }\n : undefined\n }\n className={cn(\n \"sticky top-0 z-10 bg-surface-2 align-middle backdrop-blur-sm\",\n compact ? \"w-8 px-2 py-1\" : \"w-10 px-3 py-2\",\n )}\n >\n <input\n type=\"checkbox\"\n className={cn(\"align-middle accent-indigo-600\", compact ? \"size-3.5\" : \"size-4\")}\n checked={selection.allSelected}\n ref={(el) => {\n if (el) el.indeterminate = selection.someSelected && !selection.allSelected;\n }}\n onChange={(e) => selection.onToggleAll(e.target.checked)}\n aria-label={labels.selectAllRows}\n />\n </th>\n )}\n {visibleColumns.map((col) => {\n // In server mode the utilities stay hidden unless the caller\n // takes them over and feeds them back into the server query.\n const sortable = (!isServer || hasSortCallback) && !!col.sortBy;\n const filter = !isServer || hasFilterCallback ? resolveFilter(col) : null;\n const filterState = filters[col.key] ?? (filter ? defaultFilterState(filter) : undefined);\n const sortIdx = sorts.findIndex((s) => s.key === col.key);\n const active = sortIdx !== -1;\n const Icon = active ? (sorts[sortIdx].dir === \"asc\" ? ArrowUp : ArrowDown) : ArrowUpDown;\n const filterActive = filterState && isFilterActive(filterState);\n // Logical, and a legacy `text-right` still counts — see `logicalAlign`.\n const headClass = logicalAlign(col.headClassName);\n const isEndAligned = !!headClass && END_ALIGN.test(headClass);\n const width = widths[col.key];\n const ownHead = splitCellProps(col.headProps);\n const pinnedHead = width ? { width, minWidth: width, maxWidth: width } : undefined;\n return (\n <th\n {...ownHead.attrs}\n key={col.key}\n ref={(el) => {\n if (el) headRefs.set(col.key, el);\n else headRefs.delete(col.key);\n }}\n scope=\"col\"\n // Only a header the user can actually sort says anything: an\n // `aria-sort` on a plain column is a control that is not there,\n // and a persisted or URL sort can name a column with no `sortBy`\n // (the sort chain drops it silently) — which would otherwise\n // claim a sort the table is not applying.\n //\n // \"none\" on the rest is the half that was missing. Without it a\n // sortable header is indistinguishable from a plain one until the\n // user presses it. Secondary criteria read \"none\" too: ARIA allows\n // exactly one sorted column, and the priority is carried by the\n // badge and by what the header button announces on press.\n aria-sort={\n sortable\n ? sortIdx === 0\n ? sorts[0].dir === \"asc\"\n ? \"ascending\"\n : \"descending\"\n : \"none\"\n : undefined\n }\n style={\n ownHead.style || pinnedHead || lockedWidths?.[col.key] !== undefined\n ? {\n ...ownHead.style,\n ...(lockedWidths?.[col.key] !== undefined ? { minWidth: lockedWidths[col.key] } : null),\n ...pinnedHead,\n }\n : undefined\n }\n className={cn(\n \"relative font-medium align-middle whitespace-nowrap\",\n compact ? \"px-2 py-1\" : \"px-3 py-2\",\n // Keep the header visible while the user scrolls the page.\n // Each <th> carries its own bg so the row doesn't render\n // transparent over the data rows underneath.\n \"sticky top-0 z-10 bg-surface-2 backdrop-blur-sm\",\n col.grow && GROW_HEAD_CLASS,\n logicalAlign(ownHead.className),\n headClass,\n )}\n >\n <div\n className={cn(\n \"flex items-center gap-1\",\n isEndAligned && \"flex-row-reverse\",\n )}\n >\n <Tooltip label={sortable && multiSort ? labels.sortHint : undefined} portal>\n <button\n type=\"button\"\n onClick={(e) => sortable && toggleSort(col.key, e.shiftKey)}\n disabled={!sortable}\n className={cn(\n \"flex items-center gap-1 text-start\",\n sortable && \"hover:text-primary\",\n )}\n >\n <span>{col.header}</span>\n {sortable && (\n <Icon className={cn(\"size-3\", active ? \"opacity-100\" : \"opacity-40\")} />\n )}\n {/* Priority badge, only meaningful with 2+ sort keys */}\n {active && sorts.length > 1 && (\n <span className=\"text-micro font-semibold leading-none text-brand\">\n {sortIdx + 1}\n </span>\n )}\n </button>\n </Tooltip>\n {filter && (\n <Popover\n // 420 px at Normal, in rem so the calendar grows with its\n // text (§3.2); Popover caps it at the viewport.\n width={filter.type === \"date\" ? \"26.25rem\" : undefined}\n // The panel is THIS column's filter, and says so. Unnamed it was\n // announced as the kit-wide fallback, \"Popover\"; then as \"Filter\"\n // in every column alike (round 0.34 Wave 2, BUG-7c).\n labels={{ panel: labels.filterColumn(columnLabel(col, labels), false) }}\n trigger={({ open, toggle, ref }) => (\n <button\n type=\"button\"\n ref={ref}\n onClick={toggle}\n // Named for its column, and for being filtered: the tint\n // alone said that to nobody who cannot see it (BUG-7c).\n aria-label={labels.filterColumn(columnLabel(col, labels), Boolean(filterActive))}\n aria-haspopup=\"dialog\"\n aria-expanded={open}\n className={cn(\n \"rounded p-1 transition-colors\",\n filterActive\n ? \"bg-brand-soft text-brand-muted\"\n : \"text-placeholder hover:text-secondary hover:bg-hover\",\n )}\n >\n <Filter className=\"size-3\" />\n </button>\n )}\n >\n {() => (\n <FilterPopover\n column={col}\n state={filterState ?? defaultFilterState(filter)}\n onChange={(next) => setFilterValue(col.key, next)}\n onClear={() => clearFilter(col.key)}\n selectOptions={selectOptionsByKey[col.key] ?? []}\n labels={labels}\n locale={locale}\n />\n )}\n </Popover>\n )}\n </div>\n {/* A `grow` column takes the rest: no width of its own to drag (0.48, KS N2). */}\n {resizable && !isTableOwnColumn(col.key) && !col.grow && (\n /* eslint-disable-next-line jsx-a11y/no-noninteractive-element-interactions -- a\n pointer-only drag handle for a visual preference (column width); it is\n deliberately not a tab stop, and no content or action depends on it. */\n <span\n role=\"separator\"\n aria-orientation=\"vertical\"\n onPointerDown={(e) => startResize(e, col.key)}\n onPointerMove={moveResize}\n onPointerUp={(e) => finishResize(e, true)}\n onPointerCancel={(e) => finishResize(e, false)}\n onDoubleClick={(e) => {\n e.preventDefault();\n setWidths((w) => {\n const next = { ...w };\n delete next[col.key];\n return next;\n });\n }}\n // `touch-none`: without it the browser claims a finger drag for\n // scrolling before the first `pointermove` ever arrives.\n // `end-0` and a translate that flips with it: the handle sits on the column's\n // trailing edge, which is its left one in a right-to-left table.\n className=\"absolute end-0 top-0 z-10 h-full w-1.5 -translate-x-1/2 rtl:translate-x-1/2 cursor-col-resize select-none touch-none hover:bg-brand-soft-hover\"\n />\n )}\n </th>\n );\n })}\n </tr>\n </thead>\n );\n}\n"],"mappings":"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","names":[]}
@@ -10,8 +10,10 @@ import type { ReactNode } from "react";
10
10
  * the content (a full-width disclosure header paints its own fill) and moving with the
11
11
  * cell; a collapsed table's row border would stay behind at the row's place.
12
12
  */
13
- export declare function DataTableLeadingRow({ colSpan, sticky, children, }: {
13
+ export declare function DataTableLeadingRow({ colSpan, sticky, ariaRowIndex, children, }: {
14
14
  colSpan: number;
15
15
  sticky?: boolean;
16
+ /** Under `virtualize` (0.48.2). */
17
+ ariaRowIndex?: number;
16
18
  children: ReactNode;
17
19
  }): import("react").JSX.Element;
@@ -5,6 +5,7 @@ import { cn } from "../lib/cn.js";
5
5
  function DataTableLeadingRow({
6
6
  colSpan,
7
7
  sticky = false,
8
+ ariaRowIndex,
8
9
  children
9
10
  }) {
10
11
  const cellRef = useRef(null);
@@ -20,7 +21,7 @@ function DataTableLeadingRow({
20
21
  observer.observe(head);
21
22
  return () => observer.disconnect();
22
23
  }, [sticky]);
23
- return /* @__PURE__ */ jsx("tr", { className: cn(!sticky && "border-t border-subtle"), children: /* @__PURE__ */ jsx(
24
+ return /* @__PURE__ */ jsx("tr", { "aria-rowindex": ariaRowIndex, className: cn(!sticky && "border-t border-subtle"), children: /* @__PURE__ */ jsx(
24
25
  "td",
25
26
  {
26
27
  ref: cellRef,
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/components/data-table-leading-row.tsx"],"sourcesContent":["import { useLayoutEffect, useRef, useState } from \"react\";\nimport type { ReactNode } from \"react\";\nimport { cn } from \"../lib/cn\";\n\n/**\n * The desktop table's `leadingRow`: one full-width cell above the data rows. With\n * `sticky` (0.48, keksdose dev #603) the cell is pinned under the sticky header while the\n * rows scroll — `top` is the header's height, measured and followed (a header that wraps\n * at Large text grows). On the cell, not the `<tr>`: position:sticky on a table row is not\n * reliable across browsers, the reason the header's sticky sits on each `<th>`. Opaque\n * (`bg-surface`), so the rows pass beneath it; `z-[5]`, under the header's `z-10` and over\n * the rows' own sticky cells. The line over it is the cell's own `::before`, drawn over\n * the content (a full-width disclosure header paints its own fill) and moving with the\n * cell; a collapsed table's row border would stay behind at the row's place.\n */\nexport function DataTableLeadingRow({\n colSpan,\n sticky = false,\n children,\n}: {\n colSpan: number;\n sticky?: boolean;\n children: ReactNode;\n}) {\n const cellRef = useRef<HTMLTableCellElement>(null);\n const [top, setTop] = useState(0);\n useLayoutEffect(() => {\n if (!sticky) return;\n const head = cellRef.current?.closest(\"table\")?.tHead;\n if (!head) return;\n const measure = () => setTop(head.getBoundingClientRect().height);\n measure();\n if (typeof ResizeObserver === \"undefined\") return;\n const observer = new ResizeObserver(measure);\n observer.observe(head);\n return () => observer.disconnect();\n }, [sticky]);\n return (\n <tr className={cn(!sticky && \"border-t border-subtle\")}>\n <td\n ref={cellRef}\n colSpan={colSpan}\n data-leading-row={sticky ? \"sticky\" : \"\"}\n className={cn(\n \"p-0\",\n sticky &&\n \"sticky z-[5] bg-surface before:pointer-events-none before:absolute before:inset-x-0 before:top-0 before:z-[1] before:h-px before:bg-[var(--border)] before:content-['']\",\n )}\n style={sticky ? { top } : undefined}\n >\n {children}\n </td>\n </tr>\n );\n}\n"],"mappings":";AAuCM;AAvCN,SAAS,iBAAiB,QAAQ,gBAAgB;AAElD,SAAS,UAAU;AAaZ,SAAS,oBAAoB;AAAA,EAClC;AAAA,EACA,SAAS;AAAA,EACT;AACF,GAIG;AACD,QAAM,UAAU,OAA6B,IAAI;AACjD,QAAM,CAAC,KAAK,MAAM,IAAI,SAAS,CAAC;AAChC,kBAAgB,MAAM;AACpB,QAAI,CAAC,OAAQ;AACb,UAAM,OAAO,QAAQ,SAAS,QAAQ,OAAO,GAAG;AAChD,QAAI,CAAC,KAAM;AACX,UAAM,UAAU,MAAM,OAAO,KAAK,sBAAsB,EAAE,MAAM;AAChE,YAAQ;AACR,QAAI,OAAO,mBAAmB,YAAa;AAC3C,UAAM,WAAW,IAAI,eAAe,OAAO;AAC3C,aAAS,QAAQ,IAAI;AACrB,WAAO,MAAM,SAAS,WAAW;AAAA,EACnC,GAAG,CAAC,MAAM,CAAC;AACX,SACE,oBAAC,QAAG,WAAW,GAAG,CAAC,UAAU,wBAAwB,GACnD;AAAA,IAAC;AAAA;AAAA,MACC,KAAK;AAAA,MACL;AAAA,MACA,oBAAkB,SAAS,WAAW;AAAA,MACtC,WAAW;AAAA,QACT;AAAA,QACA,UACE;AAAA,MACJ;AAAA,MACA,OAAO,SAAS,EAAE,IAAI,IAAI;AAAA,MAEzB;AAAA;AAAA,EACH,GACF;AAEJ;","names":[]}
1
+ {"version":3,"sources":["../../src/components/data-table-leading-row.tsx"],"sourcesContent":["import { useLayoutEffect, useRef, useState } from \"react\";\nimport type { ReactNode } from \"react\";\nimport { cn } from \"../lib/cn\";\n\n/**\n * The desktop table's `leadingRow`: one full-width cell above the data rows. With\n * `sticky` (0.48, keksdose dev #603) the cell is pinned under the sticky header while the\n * rows scroll — `top` is the header's height, measured and followed (a header that wraps\n * at Large text grows). On the cell, not the `<tr>`: position:sticky on a table row is not\n * reliable across browsers, the reason the header's sticky sits on each `<th>`. Opaque\n * (`bg-surface`), so the rows pass beneath it; `z-[5]`, under the header's `z-10` and over\n * the rows' own sticky cells. The line over it is the cell's own `::before`, drawn over\n * the content (a full-width disclosure header paints its own fill) and moving with the\n * cell; a collapsed table's row border would stay behind at the row's place.\n */\nexport function DataTableLeadingRow({\n colSpan,\n sticky = false,\n ariaRowIndex,\n children,\n}: {\n colSpan: number;\n sticky?: boolean;\n /** Under `virtualize` (0.48.2). */\n ariaRowIndex?: number;\n children: ReactNode;\n}) {\n const cellRef = useRef<HTMLTableCellElement>(null);\n const [top, setTop] = useState(0);\n useLayoutEffect(() => {\n if (!sticky) return;\n const head = cellRef.current?.closest(\"table\")?.tHead;\n if (!head) return;\n const measure = () => setTop(head.getBoundingClientRect().height);\n measure();\n if (typeof ResizeObserver === \"undefined\") return;\n const observer = new ResizeObserver(measure);\n observer.observe(head);\n return () => observer.disconnect();\n }, [sticky]);\n return (\n <tr aria-rowindex={ariaRowIndex} className={cn(!sticky && \"border-t border-subtle\")}>\n <td\n ref={cellRef}\n colSpan={colSpan}\n data-leading-row={sticky ? \"sticky\" : \"\"}\n className={cn(\n \"p-0\",\n sticky &&\n \"sticky z-[5] bg-surface before:pointer-events-none before:absolute before:inset-x-0 before:top-0 before:z-[1] before:h-px before:bg-[var(--border)] before:content-['']\",\n )}\n style={sticky ? { top } : undefined}\n >\n {children}\n </td>\n </tr>\n );\n}\n"],"mappings":";AA0CM;AA1CN,SAAS,iBAAiB,QAAQ,gBAAgB;AAElD,SAAS,UAAU;AAaZ,SAAS,oBAAoB;AAAA,EAClC;AAAA,EACA,SAAS;AAAA,EACT;AAAA,EACA;AACF,GAMG;AACD,QAAM,UAAU,OAA6B,IAAI;AACjD,QAAM,CAAC,KAAK,MAAM,IAAI,SAAS,CAAC;AAChC,kBAAgB,MAAM;AACpB,QAAI,CAAC,OAAQ;AACb,UAAM,OAAO,QAAQ,SAAS,QAAQ,OAAO,GAAG;AAChD,QAAI,CAAC,KAAM;AACX,UAAM,UAAU,MAAM,OAAO,KAAK,sBAAsB,EAAE,MAAM;AAChE,YAAQ;AACR,QAAI,OAAO,mBAAmB,YAAa;AAC3C,UAAM,WAAW,IAAI,eAAe,OAAO;AAC3C,aAAS,QAAQ,IAAI;AACrB,WAAO,MAAM,SAAS,WAAW;AAAA,EACnC,GAAG,CAAC,MAAM,CAAC;AACX,SACE,oBAAC,QAAG,iBAAe,cAAc,WAAW,GAAG,CAAC,UAAU,wBAAwB,GAChF;AAAA,IAAC;AAAA;AAAA,MACC,KAAK;AAAA,MACL;AAAA,MACA,oBAAkB,SAAS,WAAW;AAAA,MACtC,WAAW;AAAA,QACT;AAAA,QACA,UACE;AAAA,MACJ;AAAA,MACA,OAAO,SAAS,EAAE,IAAI,IAAI;AAAA,MAEzB;AAAA;AAAA,EACH,GACF;AAEJ;","names":[]}
@@ -15,7 +15,49 @@ export declare function StackedFields<T>({ columns, row, compact }: {
15
15
  columns: DataTableColumn<T>[];
16
16
  row: T;
17
17
  compact: boolean;
18
- }): import("react").JSX.Element;
18
+ }): import("react").JSX.Element | null;
19
+ /** The most fields the default phone card shows under its title (0.49 KIT-7). */
20
+ export declare const MOBILE_CARD_FIELDS = 3;
21
+ /**
22
+ * The default phone card's columns (0.49 KIT-7), out of the phone's columns (visible,
23
+ * not `mobileHidden`): the title — `mobile: "primary"` or `mobilePrimary`, else the
24
+ * first that is not a status — the `role: "status"` columns for beside it, at most
25
+ * {@link MOBILE_CARD_FIELDS} fields: those marked `mobile: "secondary"` when any is,
26
+ * else the first after the title, and the `role: "actions"` columns, drawn after the
27
+ * fields. An actions column is never the title and never one of the fields, so the
28
+ * fields and the marks leave it on the card; only `mobileHidden` / `mobile: "hidden"`
29
+ * (which keep it out of `columns`) remove it.
30
+ */
31
+ export declare function mobileCardColumns<T>(columns: DataTableColumn<T>[]): {
32
+ primary: DataTableColumn<T> | null;
33
+ status: DataTableColumn<T>[];
34
+ fields: DataTableColumn<T>[];
35
+ actions: DataTableColumn<T>[];
36
+ };
37
+ /** A column whose cell keeps its own clicks — `noRowLink`, or `role: "actions"`: no row
38
+ * link may wrap it, and a card that draws it is not a link itself. */
39
+ export declare function keepsOwnClicks<T>(col: DataTableColumn<T>): boolean;
40
+ /**
41
+ * A development build's note on the default phone card (0.49 KIT-7): a `noRowLink`
42
+ * column past its three fields. Such a column holds the cell's own controls — a per-row
43
+ * delete, a link — and the card leaves it out with them, so on a phone the action is
44
+ * gone. Never for a `role: "actions"` column, which the card draws after its fields.
45
+ * Read off the declared `columns` (not the ones a person or the width hid), and only
46
+ * while the table lets the card choose: `enabled` is false with a `mobileCard` or
47
+ * `mobileDetailsInRow` (the column is then in the opened row), and a column marked
48
+ * `mobile: "secondary"` means the app chose. Once per table and column key; `name`
49
+ * (the table's `storageKey` or `urlSync` prefix) says which table, else its title
50
+ * column does.
51
+ */
52
+ export declare function useCardColumnWarnings<T>(columns: DataTableColumn<T>[], enabled: boolean, name?: string): void;
53
+ /**
54
+ * Whether a rendered cell shows nothing worth a line (0.49 KIT-7): `null`, `undefined`,
55
+ * a boolean (React draws neither), `""` or blank text, and the formatters' empty value
56
+ * "—" ({@link EMPTY_FORMATTED_VALUE}), and `booleanColumn`'s mark for an unset value.
57
+ * Any other element is never empty here — its output is the component's — so a column
58
+ * whose empty value should drop off the card returns `null` (or "—") from `cell` for it.
59
+ */
60
+ export declare function isEmptyCell(value: ReactNode): boolean;
19
61
  /**
20
62
  * The card list's sort control: a "Sort by" select and a direction button.
21
63
  *
@@ -65,7 +107,11 @@ export interface MobileRowProps<T> {
65
107
  mobileCardLinkable: boolean;
66
108
  mobileCard: DataTableProps<T>["mobileCard"];
67
109
  mobilePrimaryCol: DataTableColumn<T> | null;
110
+ /** The `role: "status"` columns, drawn as chips beside the title (0.49 KIT-7). */
111
+ mobileStatusColumns: DataTableColumn<T>[];
68
112
  mobileSecondaryColumns: DataTableColumn<T>[];
113
+ /** The `role: "actions"` columns, drawn after the fields at the card's end (0.49 KIT-7). */
114
+ mobileActionColumns: DataTableColumn<T>[];
69
115
  mobileExpandAsDialog: boolean;
70
116
  compact: boolean;
71
117
  /** The text size is Large or Extra large (§4). */
@@ -86,7 +132,7 @@ export interface MobileRowSelect {
86
132
  }
87
133
  /** One mobile card. Shared by the flat list and the grouped list, so both draw
88
134
  * identical row markup. */
89
- export declare function MobileRow<T>({ row, isExpanded, openedRowContent, opensRow, rowClassName, rowAttributes, onRowClick, rowHref, swipeActionsFor, mobileDir, mobileCardLinkable, mobileCard, mobilePrimaryCol, mobileSecondaryColumns, mobileExpandAsDialog, compact, large, select, }: MobileRowProps<T>): import("react").JSX.Element;
135
+ export declare function MobileRow<T>({ row, isExpanded, openedRowContent, opensRow, rowClassName, rowAttributes, onRowClick, rowHref, swipeActionsFor, mobileDir, mobileCardLinkable, mobileCard, mobilePrimaryCol, mobileStatusColumns, mobileSecondaryColumns, mobileActionColumns, mobileExpandAsDialog, compact, large, select, }: MobileRowProps<T>): import("react").JSX.Element;
90
136
  /**
91
137
  * The card list's way into and out of selection mode for a keyboard and a screen
92
138
  * reader — the long press is a touch gesture, and a gesture alone is no path at all.
@@ -1,6 +1,6 @@
1
1
  "use client";
2
2
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
3
- import { Fragment as Fragment2, useEffect, useId, useMemo, useRef, useState } from "react";
3
+ import { Fragment as Fragment2, isValidElement, useEffect, useId, useMemo, useRef, useState } from "react";
4
4
  import { ArrowDownWideNarrow, ArrowUpNarrowWide, ChevronDown, ChevronRight, Filter } from "lucide-react";
5
5
  import { FOCUS_RING } from "./focus-ring.js";
6
6
  import { IconButton } from "./icon-button.js";
@@ -12,6 +12,9 @@ import { FilterPopover } from "./data-table-filter-popover.js";
12
12
  import { SwipeableRow } from "./swipeable-row.js";
13
13
  import { DialogFrame } from "./dialog-frame.js";
14
14
  import { Checkbox } from "./checkbox.js";
15
+ import { Chip } from "./chip.js";
16
+ import { BooleanMark } from "./data-table-cells.js";
17
+ import { EMPTY_FORMATTED_VALUE } from "../lib/format.js";
15
18
  import { useLongPress } from "../hooks/use-long-press.js";
16
19
  import { RowLink, cleanAttrs, columnLabel, fromOutside, fromOwnControl } from "./data-table-shared.js";
17
20
  function dataAttrsOf(props) {
@@ -29,25 +32,77 @@ function physicalSwipe(actions, dir) {
29
32
  }
30
33
  const CARD_NO_TRUNCATE = "large:[&_.truncate]:whitespace-normal large:[&_.truncate]:[overflow-wrap:anywhere]";
31
34
  function StackedFields({ columns, row, compact }) {
35
+ const fields = filledFields(columns, row);
36
+ if (fields.length === 0) return null;
32
37
  return /* @__PURE__ */ jsx(
33
38
  "dl",
34
39
  {
35
40
  "data-card-fields": "stacked",
36
41
  className: cn("relative flex min-w-0 flex-col", compact ? "gap-1.5 text-xs" : "gap-2.5 text-sm", CARD_NO_TRUNCATE),
37
- children: columns.map((col) => /* @__PURE__ */ jsxs("div", { className: "min-w-0", children: [
42
+ children: fields.map(({ col, value }) => /* @__PURE__ */ jsxs("div", { className: "min-w-0", children: [
38
43
  /* @__PURE__ */ jsx("dt", { className: "text-xs uppercase tracking-wide text-muted", children: col.header }),
39
44
  /* @__PURE__ */ jsx(
40
45
  "dd",
41
46
  {
42
47
  ...dataAttrsOf(col.cellProps?.(row)),
43
48
  className: "mt-0.5 min-w-0 break-words text-secondary",
44
- children: col.cell(row)
49
+ children: value
45
50
  }
46
51
  )
47
52
  ] }, col.key))
48
53
  }
49
54
  );
50
55
  }
56
+ const MOBILE_CARD_FIELDS = 3;
57
+ function mobileCardColumns(columns) {
58
+ const titles = columns.filter((c) => c.role !== "actions");
59
+ const primary = titles.find((c) => c.mobile === "primary") ?? titles.find((c) => c.mobilePrimary) ?? titles.find((c) => c.role !== "status") ?? titles[0] ?? null;
60
+ const status = columns.filter((c) => c !== primary && c.role === "status");
61
+ const actions = columns.filter((c) => c.role === "actions");
62
+ const rest = titles.filter((c) => c !== primary && c.role !== "status");
63
+ const marked = rest.filter((c) => c.mobile === "secondary");
64
+ return { primary, status, fields: (marked.length > 0 ? marked : rest).slice(0, MOBILE_CARD_FIELDS), actions };
65
+ }
66
+ function keepsOwnClicks(col) {
67
+ return !!col.noRowLink || col.role === "actions";
68
+ }
69
+ function devWarnings() {
70
+ return Boolean(import.meta.env?.DEV) && !import.meta.env?.VITEST;
71
+ }
72
+ function useCardColumnWarnings(columns, enabled, name) {
73
+ const warned = useRef(null);
74
+ useEffect(() => {
75
+ if (!enabled || !devWarnings()) return;
76
+ const phone = columns.filter((c) => !c.mobileHidden && c.mobile !== "hidden");
77
+ if (phone.some((c) => c.mobile === "secondary")) return;
78
+ const { primary, status, fields, actions } = mobileCardColumns(phone);
79
+ const onCard = /* @__PURE__ */ new Set([primary, ...status, ...fields, ...actions]);
80
+ const table = name ? ` "${name}"` : primary ? ` (title column "${primary.key}")` : "";
81
+ warned.current ??= /* @__PURE__ */ new Set();
82
+ for (const col of phone) {
83
+ if (onCard.has(col) || !col.noRowLink || warned.current.has(col.key)) continue;
84
+ warned.current.add(col.key);
85
+ console.warn(
86
+ `[ui-kit] DataTable${table}: column "${col.key}" (noRowLink) is past the phone card's three fields and leaves the card \u2014 give it role: "actions" (drawn after the fields), or move its action to rowActions.`
87
+ );
88
+ }
89
+ }, [columns, enabled, name]);
90
+ }
91
+ function isEmptyCell(value) {
92
+ if (value === null || value === void 0 || typeof value === "boolean") return true;
93
+ if (isValidElement(value) && value.type === BooleanMark) return value.props.value == null;
94
+ if (typeof value === "string") {
95
+ const text = value.trim();
96
+ return text === "" || text === EMPTY_FORMATTED_VALUE;
97
+ }
98
+ return false;
99
+ }
100
+ function filledFields(columns, row) {
101
+ return columns.map((col) => ({ col, value: col.cell(row) })).filter(({ value }) => !isEmptyCell(value));
102
+ }
103
+ function StatusChip({ value }) {
104
+ return typeof value === "string" || typeof value === "number" ? /* @__PURE__ */ jsx(Chip, { size: "sm", children: value }) : /* @__PURE__ */ jsx(Fragment, { children: value });
105
+ }
51
106
  function MobileSort({
52
107
  columns,
53
108
  sorts,
@@ -118,7 +173,9 @@ function MobileRow({
118
173
  mobileCardLinkable,
119
174
  mobileCard,
120
175
  mobilePrimaryCol,
176
+ mobileStatusColumns,
121
177
  mobileSecondaryColumns,
178
+ mobileActionColumns,
122
179
  mobileExpandAsDialog,
123
180
  compact,
124
181
  large,
@@ -162,6 +219,9 @@ function MobileRow({
162
219
  // is not selectable on a phone where the table offers selection.
163
220
  select && "select-none [-webkit-touch-callout:none]"
164
221
  );
222
+ const fields = mobileCard ? [] : filledFields(mobileSecondaryColumns, row);
223
+ const statuses = mobileCard ? [] : filledFields(mobileStatusColumns, row);
224
+ const actionCells = mobileCard ? [] : filledFields(mobileActionColumns, row);
165
225
  const cardInner = /* @__PURE__ */ jsxs(Fragment, { children: [
166
226
  /* @__PURE__ */ jsx(
167
227
  "div",
@@ -175,31 +235,43 @@ function MobileRow({
175
235
  !mobileCard && CARD_NO_TRUNCATE
176
236
  ),
177
237
  children: mobileCard ? mobileCard(row) : /* @__PURE__ */ jsxs(Fragment, { children: [
178
- mobilePrimaryCol && /* @__PURE__ */ jsx("div", { ...dataAttrsOf(mobilePrimaryCol.cellProps?.(row)), className: "min-w-0 break-words font-medium", children: mobilePrimaryCol.cell(row) }),
179
- mobileSecondaryColumns.length > 0 && large ? (
238
+ (mobilePrimaryCol || statuses.length > 0) && /* @__PURE__ */ jsxs("div", { "data-card-head": "", className: "flex min-w-0 items-start justify-between gap-2", children: [
239
+ mobilePrimaryCol && /* @__PURE__ */ jsx(
240
+ "div",
241
+ {
242
+ ...dataAttrsOf(mobilePrimaryCol.cellProps?.(row)),
243
+ "data-card-title": "",
244
+ className: "min-w-0 flex-1 break-words font-medium",
245
+ children: mobilePrimaryCol.cell(row)
246
+ }
247
+ ),
248
+ statuses.length > 0 && /* @__PURE__ */ jsx("div", { className: "flex shrink-0 flex-wrap items-center justify-end gap-1", children: statuses.map(({ col, value }) => /* @__PURE__ */ jsx("span", { ...dataAttrsOf(col.cellProps?.(row)), "data-card-status": "", className: "inline-flex", children: /* @__PURE__ */ jsx(StatusChip, { value }) }, col.key)) })
249
+ ] }),
250
+ fields.length > 0 && large ? (
180
251
  // §4: each label ABOVE its value at Large. Side by side, a label column
181
252
  // sized to the widest label leaves a value a sliver of a 240 px card.
182
253
  /* @__PURE__ */ jsx(StackedFields, { columns: mobileSecondaryColumns, row, compact })
183
- ) : mobileSecondaryColumns.length > 0 && /* @__PURE__ */ jsx(
254
+ ) : fields.length > 0 && /* @__PURE__ */ jsx(
184
255
  "dl",
185
256
  {
186
257
  className: cn(
187
258
  "grid grid-cols-[auto_1fr] gap-x-3",
188
259
  compact ? "gap-y-0.5 text-xs" : "gap-y-1 text-sm"
189
260
  ),
190
- children: mobileSecondaryColumns.map((col) => /* @__PURE__ */ jsxs(Fragment2, { children: [
261
+ children: fields.map(({ col, value }) => /* @__PURE__ */ jsxs(Fragment2, { children: [
191
262
  /* @__PURE__ */ jsx("dt", { className: "text-xs uppercase tracking-wide text-muted self-center", children: col.header }),
192
263
  /* @__PURE__ */ jsx(
193
264
  "dd",
194
265
  {
195
266
  ...dataAttrsOf(col.cellProps?.(row)),
196
267
  className: "min-w-0 text-secondary self-center",
197
- children: col.cell(row)
268
+ children: value
198
269
  }
199
270
  )
200
271
  ] }, col.key))
201
272
  }
202
- )
273
+ ),
274
+ actionCells.length > 0 && /* @__PURE__ */ jsx("div", { "data-card-actions": "", className: "flex min-w-0 flex-wrap items-center justify-end gap-2", children: actionCells.map(({ col, value }) => /* @__PURE__ */ jsx("div", { ...dataAttrsOf(col.cellProps?.(row)), className: "flex min-w-0 justify-end", children: value }, col.key)) })
203
275
  ] })
204
276
  }
205
277
  ),
@@ -565,10 +637,15 @@ function MobileFilters({
565
637
  ] });
566
638
  }
567
639
  export {
640
+ MOBILE_CARD_FIELDS,
568
641
  MobileFilters,
569
642
  MobileRow,
570
643
  MobileSelectBar,
571
644
  MobileSort,
572
- StackedFields
645
+ StackedFields,
646
+ isEmptyCell,
647
+ keepsOwnClicks,
648
+ mobileCardColumns,
649
+ useCardColumnWarnings
573
650
  };
574
651
  //# sourceMappingURL=data-table-mobile.js.map