@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
@@ -1,23 +1,22 @@
1
1
  "use client";
2
2
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
3
- import { createElement } from "react";
4
3
  import { Fragment as Fragment2, useEffect, useId, useLayoutEffect, useMemo, useRef, useState } from "react";
5
4
  import { Card } from "./card.js";
6
5
  import { Spinner } from "./spinner.js";
7
6
  import { isTableOwnColumn, useRowActionColumn } from "./data-table-row-actions.js";
8
7
  import { useColumnResize } from "./data-table-resize.js";
9
8
  import { useRangeSelection } from "./data-table-selection.js";
9
+ import { columnLabel, logicalAlign, resolveDataTableLabels } from "./data-table-shared.js";
10
10
  import {
11
- RowLink,
12
- cleanAttrs,
13
- columnLabel,
14
- fromOutside,
15
- fromOwnControl,
16
- logicalAlign,
17
- resolveDataTableLabels,
18
- splitCellProps
19
- } from "./data-table-shared.js";
20
- import { MobileFilters, MobileRow, MobileSelectBar, MobileSort, StackedFields } from "./data-table-mobile.js";
11
+ MobileFilters,
12
+ MobileSelectBar,
13
+ MobileSort,
14
+ StackedFields,
15
+ keepsOwnClicks,
16
+ mobileCardColumns,
17
+ useCardColumnWarnings
18
+ } from "./data-table-mobile.js";
19
+ import { DataTableMobileRow, DataTableRow } from "./data-table-row.js";
21
20
  import { DataTableHead } from "./data-table-head.js";
22
21
  import { DataTableLeadingRow } from "./data-table-leading-row.js";
23
22
  import {
@@ -27,6 +26,7 @@ import {
27
26
  groupRows,
28
27
  useGroupBandTops
29
28
  } from "./data-table-groups.js";
29
+ import { VIRTUAL_SELECT_COL, rowNavTarget, useRowWindow } from "./data-table-virtual.js";
30
30
  import { ColumnSettingsPanel } from "./data-table-column-settings.js";
31
31
  import { useLargeText } from "../hooks/use-large-text.js";
32
32
  import { cn } from "../lib/cn.js";
@@ -48,21 +48,11 @@ const DEFAULT_DATA_TABLE_SORT_LABELS = {
48
48
  sortDescending: DEFAULT_DATA_TABLE_LABELS.sortDescending
49
49
  };
50
50
  const flipDir = (d) => d === "asc" ? "desc" : "asc";
51
- function moveRowFocus(from, key) {
52
- if (!(key in ROW_NAV_KEYS)) return false;
53
- const table = from.closest("table");
54
- if (!table) return false;
55
- const rows = Array.from(table.tBodies).flatMap(
56
- (body) => Array.from(body.children).filter(
57
- (el) => el instanceof HTMLElement && el.hasAttribute("data-row-nav")
58
- )
59
- );
60
- const i = rows.indexOf(from);
61
- const target = key === "ArrowDown" ? rows[i + 1] : key === "ArrowUp" ? rows[i - 1] : key === "Home" ? rows[0] : key === "End" ? rows[rows.length - 1] : void 0;
62
- target?.focus();
63
- return true;
64
- }
65
- const ROW_NAV_KEYS = { ArrowDown: 1, ArrowUp: 1, Home: 1, End: 1 };
51
+ const NO_BANDS = [];
52
+ const BAND_1 = [1];
53
+ const BAND_2 = [2];
54
+ const BAND_1_2 = [1, 2];
55
+ const NO_VIRTUAL_OPTIONS = {};
66
56
  import { DEFAULT_PERSIST_PREFIX, useTableState } from "./use-table-state.js";
67
57
  import { useMobileReveal } from "./use-mobile-reveal.js";
68
58
  import { GROW_CELL_CLASS, GROW_PEER_CELL_CLASS, useColumnFit } from "./data-table-fit.js";
@@ -109,6 +99,7 @@ function DataTable({
109
99
  leadingRowSticky = false,
110
100
  groupBy,
111
101
  loadMore,
102
+ virtualize = false,
112
103
  density = "comfortable",
113
104
  sortCycle = "tri",
114
105
  chrome = "full",
@@ -319,19 +310,22 @@ function DataTable({
319
310
  else setPage(p);
320
311
  announce(labels.pageChanged(p + 1, totalPages));
321
312
  };
313
+ const large = useLargeText();
322
314
  const { autoHidden, keep: keepColumn } = useColumnFit({
323
315
  columns,
324
316
  hiddenCols,
325
317
  headRefs,
326
318
  scroller: () => desktopScroller.current,
327
- enabled: !usePhoneLayout()
319
+ enabled: !usePhoneLayout(),
320
+ // Measured only when one of these changed (0.48.2): not on a ticked box.
321
+ content: [slice, columns, hiddenCols, widths, density, large]
328
322
  });
329
323
  const visibleColumns = useMemo(
330
324
  () => columns.filter((c) => !hiddenCols.has(c.key) && !autoHidden.has(c.key)),
331
325
  [columns, hiddenCols, autoHidden]
332
326
  );
333
327
  const growDrawn = visibleColumns.some((c) => c.grow);
334
- const linkColumn = rowHref ? visibleColumns.find((c) => c.mobilePrimary && !c.noRowLink) ?? visibleColumns.find((c) => !c.noRowLink) : void 0;
328
+ const linkColumn = rowHref ? visibleColumns.find((c) => c.mobilePrimary && !keepsOwnClicks(c)) ?? visibleColumns.find((c) => !keepsOwnClicks(c)) : void 0;
335
329
  const { startResize, moveResize, finishResize, autoSizeAll } = useColumnResize({
336
330
  headRefs,
337
331
  widths,
@@ -364,11 +358,28 @@ function DataTable({
364
358
  return () => ro.disconnect();
365
359
  }, [measuresVisibleWidth]);
366
360
  const edgeFadeProps = useEdgeFade(desktopScroller, edgeFade && isMdUp);
367
- const mobileColumns = visibleColumns.filter((c) => !c.mobileHidden);
368
- const mobilePrimaryCol = mobileColumns.find((c) => c.mobilePrimary) ?? mobileColumns[0] ?? null;
369
- const mobileSecondaryColumns = mobileColumns.filter((c) => c !== mobilePrimaryCol);
370
- const mobileDetailColumns = mobileDetailsInRow ? visibleColumns.filter((c) => c.mobileHidden) : [];
371
- const large = useLargeText();
361
+ const mobileColumns = useMemo(
362
+ () => visibleColumns.filter((c) => !c.mobileHidden && c.mobile !== "hidden"),
363
+ [visibleColumns]
364
+ );
365
+ const {
366
+ primary: mobilePrimaryCol,
367
+ status: mobileStatusColumns,
368
+ fields: mobileSecondaryColumns,
369
+ actions: mobileActionColumns
370
+ } = useMemo(() => mobileCardColumns(mobileColumns), [mobileColumns]);
371
+ useCardColumnWarnings(
372
+ columns,
373
+ !mobileCard && !mobileDetailsInRow,
374
+ storageKey ?? (typeof urlSync === "object" ? urlSync.prefix : void 0)
375
+ );
376
+ const onCard = /* @__PURE__ */ new Set([
377
+ ...mobilePrimaryCol ? [mobilePrimaryCol] : [],
378
+ ...mobileStatusColumns,
379
+ ...mobileSecondaryColumns,
380
+ ...mobileActionColumns
381
+ ]);
382
+ const mobileDetailColumns = mobileDetailsInRow ? visibleColumns.filter((c) => mobileCard ? c.mobileHidden || c.mobile === "hidden" : !onCard.has(c)) : [];
372
383
  const opensRow = !!expandedRow || mobileDetailColumns.length > 0;
373
384
  const openedRowContent = (row) => {
374
385
  const detail = expandedRow?.(row) ?? null;
@@ -378,7 +389,7 @@ function DataTable({
378
389
  detail
379
390
  ] });
380
391
  };
381
- const mobileCardLinkable = mobileColumns.every((c) => !c.noRowLink);
392
+ const mobileCardLinkable = (mobileCard ? mobileColumns : [...onCard]).every((c) => !keepsOwnClicks(c));
382
393
  const [mobileDir, setMobileDir] = useState("ltr");
383
394
  const mobileRootRef = (el) => {
384
395
  if (!el) return;
@@ -417,7 +428,7 @@ function DataTable({
417
428
  if (!anySelected) setPhoneSelectMode(false);
418
429
  }
419
430
  const phoneSelecting = !!selection && !isMdUp && (phoneSelectMode || anySelected);
420
- const { selectionAnchor, rangeClick, canSelect, selectRange } = useRangeSelection({
431
+ const { canSelect, selectRange, setAnchor, markRangeClick, takeRangeClick } = useRangeSelection({
421
432
  selection,
422
433
  slice,
423
434
  rowKey
@@ -425,49 +436,75 @@ function DataTable({
425
436
  const rowsFocusable = !!onRowClick;
426
437
  const [focusRowKey, setFocusRowKey] = useState(null);
427
438
  const tabRowKey = focusRowKey !== null && slice.some((r) => rowKey(r) === focusRowKey) ? focusRowKey : slice.length ? rowKey(slice[0]) : null;
439
+ const virtualOn = !!virtualize && isMdUp && slice.length > 0;
440
+ const focusRowRef = useRef(void 0);
441
+ const latestRowCtx = {
442
+ moveFocus: virtualOn ? (from, key) => {
443
+ const target = rowNavTarget(from, key, slice.length);
444
+ if (target === null) return false;
445
+ focusRowRef.current?.(target);
446
+ return true;
447
+ } : void 0,
448
+ onRowClick,
449
+ onToggle: selection?.onToggle,
450
+ isSelected: (row) => !!selection?.isSelected(row),
451
+ canSelect,
452
+ selectRange,
453
+ setAnchor,
454
+ markRangeClick,
455
+ takeRangeClick,
456
+ setFocusRowKey,
457
+ onLongPress: (row) => {
458
+ setPhoneSelectMode(true);
459
+ setAnchor(rowKey(row));
460
+ if (selection && !selection.isSelected(row)) selection.onToggle(row, true);
461
+ }
462
+ };
463
+ const rowCtx = useRef(latestRowCtx);
464
+ useLayoutEffect(() => {
465
+ rowCtx.current = latestRowCtx;
466
+ });
428
467
  const mobileDialogRow = mobileExpandAsDialog && !isMdUp ? mobileSlice.find((r) => isExpanded?.(r)) : void 0;
429
468
  const mobileDialogContent = mobileDialogRow ? openedRowContent(mobileDialogRow) : null;
430
469
  const dialogOpen = !!(mobileDialogRow && mobileDialogContent);
431
470
  const closeDialog = () => {
432
471
  if (mobileDialogRow) onRowClick?.(mobileDialogRow);
433
472
  };
434
- const renderMobileRow = (row) => /* @__PURE__ */ jsx(
435
- MobileRow,
436
- {
437
- row,
438
- isExpanded,
439
- openedRowContent,
440
- opensRow,
441
- rowClassName,
442
- rowAttributes,
443
- onRowClick,
444
- rowHref,
445
- swipeActionsFor,
446
- mobileDir,
447
- mobileCardLinkable,
448
- mobileCard,
449
- mobilePrimaryCol,
450
- mobileSecondaryColumns,
451
- mobileExpandAsDialog,
452
- compact,
453
- large,
454
- select: selection ? {
455
- active: phoneSelecting,
473
+ const renderMobileRow = (row) => {
474
+ const key = rowKey(row);
475
+ const expanded = isExpanded?.(row) ?? false;
476
+ return /* @__PURE__ */ jsx(
477
+ DataTableMobileRow,
478
+ {
479
+ ctx: rowCtx,
480
+ rowKey: key,
481
+ row,
482
+ expanded,
483
+ expansion: expanded ? openedRowContent(row) : null,
484
+ opensRow,
485
+ className: rowClassName?.(row),
486
+ attrs: rowAttributes?.(row),
487
+ interactive: !!onRowClick,
488
+ href: mobileCardLinkable ? rowHref?.(row) : void 0,
489
+ swipeActionsFor,
490
+ mobileDir,
491
+ mobileCardLinkable,
492
+ mobileCard,
493
+ mobilePrimaryCol,
494
+ mobileStatusColumns,
495
+ mobileSecondaryColumns,
496
+ mobileActionColumns,
497
+ mobileExpandAsDialog,
498
+ compact,
499
+ large,
500
+ selectionOn: !!selection,
501
+ selectActive: phoneSelecting,
456
502
  selectable: canSelect(row),
457
- selected: selection.isSelected(row),
458
- onToggle: (checked) => {
459
- selectionAnchor.current = rowKey(row);
460
- selection.onToggle(row, checked);
461
- },
462
- onLongPress: () => {
463
- setPhoneSelectMode(true);
464
- selectionAnchor.current = rowKey(row);
465
- if (!selection.isSelected(row)) selection.onToggle(row, true);
466
- }
467
- } : null
468
- },
469
- rowKey(row)
470
- );
503
+ selected: !!selection && selection.isSelected(row)
504
+ },
505
+ key
506
+ );
507
+ };
471
508
  const tableRef = useRef(null);
472
509
  const desktopGroups = isMdUp && levels.length > 0 ? groupRows(slice, levels) : null;
473
510
  const bandTops = useGroupBandTops(
@@ -475,6 +512,51 @@ function DataTable({
475
512
  !!desktopGroups,
476
513
  `${levels.length}:${leadingRowSticky && !!leadingRow}:${desktopGroups?.[0]?.key ?? ""}`
477
514
  );
515
+ const virtualGroups = [];
516
+ const virtualBands = [];
517
+ if (virtualOn) {
518
+ if (!desktopGroups) virtualGroups.push({ start: 0, end: slice.length, bands: NO_BANDS, first: 0 });
519
+ else
520
+ for (const g1 of desktopGroups) {
521
+ const first = virtualGroups.length;
522
+ if (!g1.children) {
523
+ virtualGroups.push({ start: g1.start, end: g1.start + g1.rows.length, bands: BAND_1, first });
524
+ virtualBands.push({ g1, g2: null });
525
+ continue;
526
+ }
527
+ g1.children.forEach((g2, j) => {
528
+ virtualGroups.push({ start: g2.start, end: g2.start + g2.rows.length, bands: j === 0 ? BAND_1_2 : BAND_2, first });
529
+ virtualBands.push({ g1, g2 });
530
+ });
531
+ }
532
+ }
533
+ const expandedIndices = [];
534
+ if (virtualOn && isExpanded && expandedRow) slice.forEach((r, i) => isExpanded(r) && expandedIndices.push(i));
535
+ const virtualColKeys = [...selection ? [VIRTUAL_SELECT_COL] : [], ...visibleColumns.map((c) => c.key)];
536
+ const virtualLockable = new Set(
537
+ virtualColKeys.filter((k) => !widths[k] && !visibleColumns.some((c) => c.key === k && c.grow))
538
+ );
539
+ const rowWindow = useRowWindow({
540
+ enabled: virtualOn,
541
+ rows: slice,
542
+ rowKey,
543
+ groups: virtualGroups,
544
+ expanded: expandedIndices,
545
+ pinnedKeys: [tabRowKey],
546
+ scrollerRef: desktopScroller,
547
+ tableRef,
548
+ options: typeof virtualize === "object" ? virtualize : NO_VIRTUAL_OPTIONS,
549
+ compact,
550
+ rowsBefore: 1 + (leadingRow ? 1 : 0),
551
+ rowsAfter: showFooter ? 1 : 0,
552
+ colKeys: virtualColKeys,
553
+ lockable: virtualLockable,
554
+ resetKey: `${Object.entries(widths).join(";")}|${density}|${large}|${slice.length}:${slice.length ? `${rowKey(slice[0])}:${rowKey(slice[slice.length - 1])}` : ""}`
555
+ });
556
+ const focusRowAt = rowWindow?.focusRow;
557
+ useLayoutEffect(() => {
558
+ focusRowRef.current = focusRowAt;
559
+ }, [focusRowAt]);
478
560
  const mobileLevel = typeof mobileGroupBy === "function" ? null : mobileGroupBy ?? null;
479
561
  const mobileKey = typeof mobileGroupBy === "function" ? mobileGroupBy : mobileGroupBy?.key;
480
562
  const mobileGroups = mobileKey ? mobileSlice.reduce((acc, row) => {
@@ -704,7 +786,13 @@ function DataTable({
704
786
  "data-fit-grow": growDrawn ? "" : void 0,
705
787
  "aria-label": labels.table,
706
788
  "aria-busy": isLoading || void 0,
789
+ "aria-rowcount": rowWindow?.rowCount,
790
+ "data-virtualized": rowWindow ? "" : void 0,
707
791
  children: [
792
+ rowWindow && /* @__PURE__ */ jsx("colgroup", { children: virtualColKeys.map((key) => {
793
+ const w = rowWindow.colWidths?.[key];
794
+ return /* @__PURE__ */ jsx("col", { style: w !== void 0 ? { width: w } : void 0 }, key);
795
+ }) }),
708
796
  /* @__PURE__ */ jsx(
709
797
  DataTableHead,
710
798
  {
@@ -729,154 +817,58 @@ function DataTable({
729
817
  clearFilter,
730
818
  startResize,
731
819
  moveResize,
732
- finishResize
820
+ finishResize,
821
+ ariaRowIndex: rowWindow ? 1 : void 0,
822
+ lockedWidths: rowWindow?.colWidths,
823
+ selectionLockKey: VIRTUAL_SELECT_COL
733
824
  }
734
825
  ),
735
826
  (() => {
736
827
  const bodyClass = cn(isLoading && slice.length > 0 && "opacity-60 transition-opacity");
737
- const leading = leadingRow ? /* @__PURE__ */ jsx(DataTableLeadingRow, { colSpan: totalColSpan, sticky: leadingRowSticky, children: leadingRow }) : null;
828
+ const leading = leadingRow ? /* @__PURE__ */ jsx(
829
+ DataTableLeadingRow,
830
+ {
831
+ colSpan: totalColSpan,
832
+ sticky: leadingRowSticky,
833
+ ariaRowIndex: rowWindow ? 2 : void 0,
834
+ children: leadingRow
835
+ }
836
+ ) : null;
738
837
  const renderRow = (row, rowIndex) => {
739
838
  const expanded = isExpanded?.(row) ?? false;
740
- const expansion = expanded ? expandedRow?.(row) : null;
741
- const rowInteractive = !!onRowClick || !!selection;
742
- const href = rowHref?.(row);
743
839
  const key = rowKey(row);
744
- const detailId = `${detailIdBase}-${rowIndex}`;
745
- const disclosure = rowsFocusable && expandedRow ? /* @__PURE__ */ jsx(
746
- "button",
840
+ return /* @__PURE__ */ jsx(
841
+ DataTableRow,
747
842
  {
748
- type: "button",
749
- tabIndex: -1,
750
- className: "sr-only-fixed",
751
- "aria-label": labels.rowDetails,
752
- "aria-expanded": expanded,
753
- "aria-controls": expansion ? detailId : void 0,
754
- onClick: () => onRowClick(row)
755
- }
756
- ) : null;
757
- return /* @__PURE__ */ jsxs(Fragment2, { children: [
758
- /* @__PURE__ */ jsxs(
759
- "tr",
760
- {
761
- ...cleanAttrs(rowAttributes?.(row)),
762
- "data-row-nav": rowsFocusable ? "" : void 0,
763
- tabIndex: rowsFocusable ? key === tabRowKey ? 0 : -1 : void 0,
764
- onFocus: rowsFocusable ? (e) => {
765
- if (e.target === e.currentTarget) setFocusRowKey(key);
766
- } : void 0,
767
- onKeyDown: rowsFocusable ? (e) => {
768
- if (e.target !== e.currentTarget) return;
769
- if (e.altKey || e.ctrlKey || e.metaKey) return;
770
- if (e.key === "Enter" || e.key === " ") {
771
- e.preventDefault();
772
- onRowClick(row);
773
- return;
774
- }
775
- if (e.shiftKey) return;
776
- if (moveRowFocus(e.currentTarget, e.key)) e.preventDefault();
777
- } : void 0,
778
- className: cn(
779
- "border-t border-subtle",
780
- rowInteractive && "cursor-pointer hover:bg-hover",
781
- // The kit's focus frame width (§5, `--focus-ring-width`), drawn as an
782
- // OUTLINE rather than FOCUS_RING's box-shadow: a shadow on a `<tr>`
783
- // is not painted by every engine, an outline is.
784
- rowsFocusable && "focus-visible:bg-hover focus-visible:outline-focus focus-visible:-outline-offset-2 focus-visible:outline-brand",
785
- rowClassName?.(row)
786
- ),
787
- onClick: rowInteractive ? (e) => {
788
- if (fromOwnControl(e.target, e.currentTarget)) return;
789
- if (fromOutside(e.target, e.currentTarget)) return;
790
- if (selection && canSelect(row)) {
791
- if (e.shiftKey) {
792
- window.getSelection?.()?.removeAllRanges();
793
- if (selectRange(rowIndex)) return;
794
- selectionAnchor.current = rowKey(row);
795
- selection.onToggle(row, !selection.isSelected(row));
796
- return;
797
- }
798
- if (e.metaKey || e.ctrlKey) {
799
- selectionAnchor.current = rowKey(row);
800
- selection.onToggle(row, !selection.isSelected(row));
801
- return;
802
- }
803
- }
804
- onRowClick?.(row);
805
- } : void 0,
806
- children: [
807
- selection && /* @__PURE__ */ jsx(
808
- "td",
809
- {
810
- className: cn("align-top", compact ? "w-8 px-2 py-1" : "w-10 px-3 py-2"),
811
- onClick: (e) => e.stopPropagation(),
812
- children: canSelect(row) && /* @__PURE__ */ jsx(
813
- "input",
814
- {
815
- type: "checkbox",
816
- className: cn("accent-indigo-600", compact ? "size-3.5" : "size-4"),
817
- checked: selection.isSelected(row),
818
- onClick: (e) => {
819
- if (e.shiftKey && selectRange(rowIndex)) {
820
- e.preventDefault();
821
- rangeClick.current = e.nativeEvent;
822
- }
823
- },
824
- onChange: (e) => {
825
- if (rangeClick.current && e.nativeEvent === rangeClick.current) {
826
- rangeClick.current = null;
827
- return;
828
- }
829
- selectionAnchor.current = rowKey(row);
830
- selection.onToggle(row, e.target.checked);
831
- },
832
- "aria-label": labels.selectRow
833
- }
834
- )
835
- }
836
- ),
837
- visibleColumns.map((col, colIndex) => {
838
- const width = widths[col.key];
839
- const own = splitCellProps(col.cellProps?.(row));
840
- const pinned = width ? { width, minWidth: width, maxWidth: width } : void 0;
841
- return /* @__PURE__ */ createElement(
842
- "td",
843
- {
844
- ...own.attrs,
845
- key: col.key,
846
- "data-col": col.key,
847
- style: own.style || pinned ? { ...own.style, ...pinned } : void 0,
848
- className: cn(
849
- "align-top",
850
- compact ? "px-2 py-1" : "px-3 py-2",
851
- width && "overflow-hidden text-ellipsis",
852
- col.grow ? GROW_CELL_CLASS : growDrawn && GROW_PEER_CELL_CLASS,
853
- logicalAlign(own.className),
854
- logicalAlign(col.className)
855
- )
856
- },
857
- colIndex === 0 && disclosure,
858
- col === linkColumn && href ? (
859
- // No `onActivate`: the click is left to bubble to the
860
- // <tr> handler above, which already owns expand-vs-select.
861
- // Calling it here as well would toggle the row twice.
862
- /* @__PURE__ */ jsx(RowLink, { href, className: "block", "data-row-link": "", children: col.cell(row) })
863
- ) : col.cell(row)
864
- );
865
- })
866
- ]
867
- }
868
- ),
869
- expansion && /* @__PURE__ */ jsx("tr", { id: disclosure ? detailId : void 0, className: "bg-surface-2 border-t border-subtle", children: /* @__PURE__ */ jsx("td", { colSpan: totalColSpan, className: "p-0", children: /* @__PURE__ */ jsx("div", { className: "w-0 min-w-full", children: /* @__PURE__ */ jsx(
870
- "div",
871
- {
872
- className: cn(
873
- "sticky start-0 w-[var(--dt-visible-w,100%)] max-w-full",
874
- compact ? "px-2 py-2" : "px-3 py-3"
875
- ),
876
- children: expansion
877
- }
878
- ) }) }) })
879
- ] }, key);
843
+ row,
844
+ rowIndex,
845
+ rowKey: key,
846
+ ctx: rowCtx,
847
+ columns: visibleColumns,
848
+ widths,
849
+ growDrawn,
850
+ linkColumn,
851
+ href: rowHref?.(row),
852
+ selectionColumn: !!selection,
853
+ selectable: canSelect(row),
854
+ selected: !!selection && selection.isSelected(row),
855
+ expanded,
856
+ expansion: expanded ? expandedRow?.(row) : null,
857
+ disclosure: rowsFocusable && !!expandedRow,
858
+ detailId: `${detailIdBase}-${rowIndex}`,
859
+ rowsFocusable,
860
+ rowInteractive: !!onRowClick || !!selection,
861
+ tabbable: key === tabRowKey,
862
+ className: rowClassName?.(row),
863
+ attrs: rowAttributes?.(row),
864
+ compact,
865
+ totalColSpan,
866
+ rowDetailsLabel: labels.rowDetails,
867
+ selectRowLabel: labels.selectRow,
868
+ ariaRowIndex: rowWindow?.ariaRow(rowIndex)
869
+ },
870
+ key
871
+ );
880
872
  };
881
873
  const emptyRow = slice.length === 0 ? /* @__PURE__ */ jsx("tr", { children: /* @__PURE__ */ jsx(
882
874
  "td",
@@ -886,14 +878,7 @@ function DataTable({
886
878
  children: isLoading ? /* @__PURE__ */ jsx(LoadingText, { label: labels.loading }) : empty ?? labels.empty
887
879
  }
888
880
  ) }) : null;
889
- if (!desktopGroups) {
890
- return /* @__PURE__ */ jsxs("tbody", { className: bodyClass, children: [
891
- leading,
892
- slice.map(renderRow),
893
- emptyRow
894
- ] });
895
- }
896
- const band = (level, def, g) => /* @__PURE__ */ jsx(
881
+ const band = (level, def, g, ariaRowIndex) => /* @__PURE__ */ jsx(
897
882
  DataTableGroupBand,
898
883
  {
899
884
  level,
@@ -901,9 +886,44 @@ function DataTable({
901
886
  top: level === 1 ? bandTops[0] : bandTops[1],
902
887
  compact,
903
888
  label: def.label ? def.label(g.key, g.rows) : g.key,
904
- end: def.end?.(g.key, g.rows)
889
+ end: def.end?.(g.key, g.rows),
890
+ ariaRowIndex
905
891
  }
906
892
  );
893
+ if (rowWindow) {
894
+ const spacer = (key, height) => /* @__PURE__ */ jsx("tr", { "aria-hidden": "true", "data-vt-spacer": "", children: /* @__PURE__ */ jsx("td", { colSpan: totalColSpan, style: { height, padding: 0, border: 0 } }) }, key);
895
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
896
+ leading && /* @__PURE__ */ jsx("tbody", { className: bodyClass, children: leading }),
897
+ rowWindow.bodies.map((b) => {
898
+ if (b.kind === "spacer")
899
+ return /* @__PURE__ */ jsx("tbody", { "data-vt-body": "", "aria-hidden": "true", children: spacer("s", b.height) }, b.key);
900
+ const g = virtualGroups[b.group];
901
+ const vb = virtualBands[b.group];
902
+ return /* @__PURE__ */ jsxs(
903
+ "tbody",
904
+ {
905
+ "data-vt-body": "",
906
+ className: bodyClass,
907
+ children: [
908
+ vb && g.bands.includes(1) && band(1, levels[0], vb.g1, rowWindow.ariaBand(b.group, 1)),
909
+ vb?.g2 && band(2, levels[1], vb.g2, rowWindow.ariaBand(b.group, 2)),
910
+ b.items.map(
911
+ (it) => it.kind === "gap" ? spacer(it.key, it.height) : renderRow(slice[it.index], it.index)
912
+ )
913
+ ]
914
+ },
915
+ vb ? `vg:${vb.g1.key}:${vb.g2?.key ?? ""}:${g.start}` : "vg"
916
+ );
917
+ })
918
+ ] });
919
+ }
920
+ if (!desktopGroups) {
921
+ return /* @__PURE__ */ jsxs("tbody", { className: bodyClass, children: [
922
+ leading,
923
+ slice.map(renderRow),
924
+ emptyRow
925
+ ] });
926
+ }
907
927
  const rowsOf = (g) => g.rows.map((r, i) => renderRow(r, g.start + i));
908
928
  return /* @__PURE__ */ jsxs(Fragment, { children: [
909
929
  (leading || emptyRow) && /* @__PURE__ */ jsxs("tbody", { className: bodyClass, children: [
@@ -935,7 +955,7 @@ function DataTable({
935
955
  "font-semibold text-primary",
936
956
  isLoading && "opacity-60 transition-opacity"
937
957
  ),
938
- children: /* @__PURE__ */ jsxs("tr", { children: [
958
+ children: /* @__PURE__ */ jsxs("tr", { "aria-rowindex": rowWindow?.rowCount, children: [
939
959
  selection && /* @__PURE__ */ jsx(
940
960
  "td",
941
961
  {