@godxjp/ui 20.0.0 → 20.1.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 (250) hide show
  1. package/README.md +8 -2
  2. package/dist/app/theme-axes.d.ts +2 -0
  3. package/dist/app/theme-axes.js +45 -0
  4. package/dist/components/charts/chart-cartesian.d.ts +2 -1
  5. package/dist/components/charts/chart-cartesian.js +52 -3
  6. package/dist/components/charts/chart-category-axis.d.ts +55 -0
  7. package/dist/components/charts/chart-category-axis.js +93 -0
  8. package/dist/components/charts/chart-frame.d.ts +10 -1
  9. package/dist/components/charts/chart-frame.js +19 -3
  10. package/dist/components/charts/compact-bar-trend.d.ts +1 -1
  11. package/dist/components/charts/compact-bar-trend.js +2 -0
  12. package/dist/components/charts/pie-chart.d.ts +1 -1
  13. package/dist/components/charts/pie-chart.js +12 -1
  14. package/dist/components/charts/recharts-peer.d.ts +49 -0
  15. package/dist/components/charts/recharts-peer.js +45 -0
  16. package/dist/components/data-display/card.d.ts +12 -1
  17. package/dist/components/data-display/card.js +22 -4
  18. package/dist/components/data-display/code-block.js +6 -2
  19. package/dist/components/data-display/data-table.d.ts +39 -4
  20. package/dist/components/data-display/data-table.js +802 -270
  21. package/dist/components/data-display/descriptions.d.ts +27 -6
  22. package/dist/components/data-display/descriptions.js +58 -17
  23. package/dist/components/data-display/index.d.ts +2 -0
  24. package/dist/components/data-display/index.js +2 -0
  25. package/dist/components/data-display/list-row.d.ts +10 -1
  26. package/dist/components/data-display/list-row.js +7 -1
  27. package/dist/components/data-display/popover.d.ts +2 -2
  28. package/dist/components/data-display/popover.js +61 -11
  29. package/dist/components/data-display/range-timeline.d.ts +38 -0
  30. package/dist/components/data-display/range-timeline.js +161 -0
  31. package/dist/components/data-display/scroll-area.js +13 -2
  32. package/dist/components/data-display/service-launcher-card.js +10 -10
  33. package/dist/components/data-display/table.d.ts +11 -3
  34. package/dist/components/data-display/table.js +48 -18
  35. package/dist/components/data-display/tree-list.js +4 -9
  36. package/dist/components/data-entry/calendar.d.ts +2 -2
  37. package/dist/components/data-entry/calendar.js +12 -1
  38. package/dist/components/data-entry/cascader.d.ts +1 -1
  39. package/dist/components/data-entry/cascader.js +188 -24
  40. package/dist/components/data-entry/color-picker.d.ts +1 -1
  41. package/dist/components/data-entry/color-picker.js +17 -5
  42. package/dist/components/data-entry/control-appearance.d.ts +64 -0
  43. package/dist/components/data-entry/control-appearance.js +39 -0
  44. package/dist/components/data-entry/control-surface.d.ts +61 -0
  45. package/dist/components/data-entry/control-surface.js +39 -0
  46. package/dist/components/data-entry/date-picker.d.ts +1 -1
  47. package/dist/components/data-entry/date-picker.js +344 -113
  48. package/dist/components/data-entry/date-range-picker.d.ts +1 -1
  49. package/dist/components/data-entry/date-range-picker.js +278 -140
  50. package/dist/components/data-entry/form-field.d.ts +1 -1
  51. package/dist/components/data-entry/form-field.js +39 -4
  52. package/dist/components/data-entry/form.d.ts +4 -0
  53. package/dist/components/data-entry/form.js +4 -2
  54. package/dist/components/data-entry/index.d.ts +7 -3
  55. package/dist/components/data-entry/index.js +10 -1
  56. package/dist/components/data-entry/input-otp.d.ts +1 -1
  57. package/dist/components/data-entry/input.d.ts +12 -34
  58. package/dist/components/data-entry/input.js +92 -24
  59. package/dist/components/data-entry/month-picker.d.ts +1 -1
  60. package/dist/components/data-entry/month-picker.js +47 -10
  61. package/dist/components/data-entry/month-range-picker.d.ts +1 -1
  62. package/dist/components/data-entry/month-range-picker.js +51 -11
  63. package/dist/components/data-entry/number-input.d.ts +7 -0
  64. package/dist/components/data-entry/number-input.js +147 -98
  65. package/dist/components/data-entry/password-input.d.ts +1 -1
  66. package/dist/components/data-entry/radio.d.ts +1 -1
  67. package/dist/components/data-entry/radio.js +61 -13
  68. package/dist/components/data-entry/search-input.d.ts +1 -1
  69. package/dist/components/data-entry/search-input.js +16 -2
  70. package/dist/components/data-entry/search-select.d.ts +2 -2
  71. package/dist/components/data-entry/search-select.js +209 -78
  72. package/dist/components/data-entry/select.d.ts +22 -4
  73. package/dist/components/data-entry/select.js +215 -163
  74. package/dist/components/data-entry/slider.d.ts +9 -1
  75. package/dist/components/data-entry/slider.js +87 -9
  76. package/dist/components/data-entry/switch.d.ts +3 -0
  77. package/dist/components/data-entry/switch.js +29 -3
  78. package/dist/components/data-entry/textarea.d.ts +14 -56
  79. package/dist/components/data-entry/textarea.js +80 -33
  80. package/dist/components/data-entry/time-picker.d.ts +2 -2
  81. package/dist/components/data-entry/time-picker.js +333 -109
  82. package/dist/components/data-entry/time-range-picker.d.ts +5 -0
  83. package/dist/components/data-entry/time-range-picker.js +89 -0
  84. package/dist/components/data-entry/transfer.d.ts +1 -1
  85. package/dist/components/data-entry/transfer.js +86 -28
  86. package/dist/components/data-entry/tree-select.d.ts +1 -1
  87. package/dist/components/data-entry/tree-select.js +201 -113
  88. package/dist/components/data-entry/tree-utils.js +7 -14
  89. package/dist/components/data-entry/upload-files.d.ts +2 -0
  90. package/dist/components/data-entry/upload-files.js +31 -0
  91. package/dist/components/data-entry/upload-request.d.ts +4 -0
  92. package/dist/components/data-entry/upload-request.js +53 -0
  93. package/dist/components/data-entry/upload-types.d.ts +28 -0
  94. package/dist/components/data-entry/upload-types.js +2 -0
  95. package/dist/components/data-entry/upload.d.ts +2 -2
  96. package/dist/components/data-entry/upload.js +475 -115
  97. package/dist/components/feedback/dialog.js +16 -19
  98. package/dist/components/general/button.d.ts +1 -1
  99. package/dist/components/general/button.js +5 -1
  100. package/dist/components/general/index.d.ts +1 -0
  101. package/dist/components/general/index.js +2 -0
  102. package/dist/components/general/typography.d.ts +3 -0
  103. package/dist/components/general/typography.js +15 -1
  104. package/dist/components/general/visually-hidden.d.ts +3 -0
  105. package/dist/components/general/visually-hidden.js +9 -0
  106. package/dist/components/layout/app-launcher.d.ts +34 -0
  107. package/dist/components/layout/app-launcher.js +228 -0
  108. package/dist/components/layout/app-shell.d.ts +2 -0
  109. package/dist/components/layout/app-shell.js +20 -7
  110. package/dist/components/layout/breadcrumb.d.ts +14 -2
  111. package/dist/components/layout/breadcrumb.js +46 -4
  112. package/dist/components/layout/flex.d.ts +1 -1
  113. package/dist/components/layout/flex.js +23 -2
  114. package/dist/components/layout/index.d.ts +3 -0
  115. package/dist/components/layout/index.js +5 -1
  116. package/dist/components/layout/org-switcher.js +20 -1
  117. package/dist/components/layout/page-container.d.ts +1 -1
  118. package/dist/components/layout/page-container.js +5 -3
  119. package/dist/components/layout/responsive-grid.d.ts +16 -2
  120. package/dist/components/layout/responsive-grid.js +29 -2
  121. package/dist/components/layout/topbar-item.d.ts +3 -0
  122. package/dist/components/layout/topbar-item.js +18 -3
  123. package/dist/components/layout/topbar.d.ts +1 -1
  124. package/dist/components/layout/topbar.js +27 -6
  125. package/dist/components/navigation/dropdown-menu.d.ts +30 -2
  126. package/dist/components/navigation/dropdown-menu.js +68 -11
  127. package/dist/components/navigation/pagination.d.ts +2 -2
  128. package/dist/components/navigation/pagination.js +155 -83
  129. package/dist/components/navigation/steps.d.ts +2 -2
  130. package/dist/components/navigation/steps.js +29 -4
  131. package/dist/components/navigation/tabs.d.ts +5 -33
  132. package/dist/components/navigation/tabs.js +232 -64
  133. package/dist/components/ui/input-otp.d.ts +26 -28
  134. package/dist/components/ui/input-otp.js +50 -7
  135. package/dist/components/ui/password-input.d.ts +36 -2
  136. package/dist/components/ui/password-input.js +27 -7
  137. package/dist/components/ui/rating.d.ts +25 -0
  138. package/dist/components/ui/rating.js +67 -27
  139. package/dist/components/ui/segmented.d.ts +9 -0
  140. package/dist/components/ui/segmented.js +17 -2
  141. package/dist/components/ui/separator.d.ts +4 -0
  142. package/dist/components/ui/separator.js +13 -0
  143. package/dist/components/ui/tag-input.d.ts +45 -0
  144. package/dist/components/ui/tag-input.js +109 -27
  145. package/dist/form/form-context.d.ts +29 -0
  146. package/dist/form/form-context.js +44 -4
  147. package/dist/form/form-field-array.d.ts +22 -0
  148. package/dist/form/form-field-array.js +50 -0
  149. package/dist/form/form-field-control.d.ts +2 -1
  150. package/dist/form/form-field-control.js +116 -40
  151. package/dist/form/form-root.d.ts +2 -1
  152. package/dist/form/form-root.js +142 -23
  153. package/dist/form/index.d.ts +3 -1
  154. package/dist/form/index.js +12 -1
  155. package/dist/i18n/messages/en.json +56 -5
  156. package/dist/i18n/messages/ja.json +56 -5
  157. package/dist/i18n/messages/vi.json +56 -5
  158. package/dist/inertia/index.d.ts +20 -0
  159. package/dist/inertia/index.js +60 -1
  160. package/dist/lib/control-styles.d.ts +25 -3
  161. package/dist/lib/control-styles.js +9 -3
  162. package/dist/lib/datetime/picker-format.d.ts +8 -0
  163. package/dist/lib/datetime/picker-format.js +54 -0
  164. package/dist/props/components/charts.prop.d.ts +21 -0
  165. package/dist/props/components/data-display.prop.d.ts +55 -4
  166. package/dist/props/components/data-entry.prop.d.ts +687 -43
  167. package/dist/props/components/form.prop.d.ts +142 -4
  168. package/dist/props/components/general.prop.d.ts +17 -1
  169. package/dist/props/components/index.d.ts +2 -2
  170. package/dist/props/components/layout.prop.d.ts +163 -6
  171. package/dist/props/components/navigation.prop.d.ts +113 -3
  172. package/dist/props/registry.d.ts +376 -10
  173. package/dist/props/registry.js +493 -9
  174. package/dist/props/vocabulary/data.prop.d.ts +179 -1
  175. package/dist/props/vocabulary/index.d.ts +4 -4
  176. package/dist/props/vocabulary/interaction.prop.d.ts +51 -2
  177. package/dist/props/vocabulary/navigation.prop.d.ts +40 -0
  178. package/dist/props/vocabulary/shared.prop.d.ts +33 -0
  179. package/dist/styles/badge-layout.css +2 -1
  180. package/dist/styles/card-layout.css +26 -4
  181. package/dist/styles/chart-layout.css +15 -0
  182. package/dist/styles/control.css +515 -14
  183. package/dist/styles/data-display-layout.css +241 -6
  184. package/dist/styles/data-entry-layout.css +13 -0
  185. package/dist/styles/layout.css +221 -4
  186. package/dist/styles/navigation-layout.css +227 -0
  187. package/dist/styles/shell-layout.css +229 -5
  188. package/dist/styles/table-layout.css +95 -6
  189. package/dist/styles/text-layout.css +5 -0
  190. package/dist/tokens/base.css +1 -0
  191. package/dist/tokens/components/badge.css +1 -0
  192. package/dist/tokens/components/chart.css +6 -0
  193. package/dist/tokens/components/control.css +79 -2
  194. package/dist/tokens/components/data-display.css +9 -0
  195. package/dist/tokens/components/descriptions.css +11 -0
  196. package/dist/tokens/components/flex.css +8 -0
  197. package/dist/tokens/components/navigation.css +55 -0
  198. package/dist/tokens/components/shell.css +50 -2
  199. package/dist/tokens/components/table.css +14 -0
  200. package/dist/tokens/semantic/layout.css +9 -0
  201. package/docs/CONSUMER-RULES.md +13 -0
  202. package/docs/DESIGN-AUTHORITY.md +43 -6
  203. package/docs/DEVELOPMENT.md +4 -3
  204. package/docs/FORMS.md +151 -6
  205. package/docs/FRAME-COVERAGE-REPORT.md +29 -16
  206. package/docs/README.md +1 -1
  207. package/docs/STANDARDS-vocabulary-tokens.md +1 -1
  208. package/docs/TESTING.md +15 -6
  209. package/docs/charts/cjk-category-axis.tsx +81 -0
  210. package/docs/data-display/card/index.tsx +22 -0
  211. package/docs/data-display/data-table/examples/antd-parity.tsx +257 -0
  212. package/docs/data-display/data-table/index.tsx +23 -0
  213. package/docs/data-display/descriptions.tsx +41 -0
  214. package/docs/data-display/scroll-area.tsx +31 -25
  215. package/docs/data-display/table.tsx +104 -45
  216. package/docs/data-display/timeline.tsx +41 -0
  217. package/docs/data-entry/date-picker.tsx +85 -0
  218. package/docs/data-entry/date-range-picker.tsx +26 -0
  219. package/docs/data-entry/form-dynamic-fields.tsx +199 -0
  220. package/docs/data-entry/form.tsx +74 -4
  221. package/docs/data-entry/input-otp.tsx +24 -0
  222. package/docs/data-entry/input.tsx +46 -1
  223. package/docs/data-entry/month-range-picker.tsx +1 -1
  224. package/docs/data-entry/select.tsx +27 -0
  225. package/docs/data-entry/switch.tsx +41 -0
  226. package/docs/data-entry/textarea.tsx +38 -0
  227. package/docs/data-entry/time-picker.tsx +85 -1
  228. package/docs/data-entry/time-range-picker.tsx +55 -0
  229. package/docs/data-entry/transfer.tsx +17 -0
  230. package/docs/data-entry/upload.tsx +56 -12
  231. package/docs/feedback/tooltip.tsx +1 -1
  232. package/docs/general/activity.tsx +2 -2
  233. package/docs/general/button/index.tsx +14 -1
  234. package/docs/general/typography.tsx +14 -1
  235. package/docs/layout/app-launcher.tsx +151 -0
  236. package/docs/layout/app-shell.tsx +11 -0
  237. package/docs/layout/flex.tsx +50 -0
  238. package/docs/layout/responsive-grid.tsx +21 -1
  239. package/docs/layout/topbar.tsx +5 -9
  240. package/docs/navigation/app-setting-picker.tsx +11 -0
  241. package/docs/navigation/breadcrumb.tsx +48 -0
  242. package/docs/navigation/dropdown-menu.tsx +21 -0
  243. package/docs/navigation/pagination.tsx +52 -0
  244. package/docs/navigation/steps.tsx +37 -0
  245. package/docs/navigation/tabs.tsx +97 -1
  246. package/docs/query/button-refetch.tsx +1 -0
  247. package/package.json +19 -2
  248. package/scripts/_agent-setup.mjs +19 -2
  249. package/scripts/init-guinea-pig.mjs +26 -4
  250. package/scripts/ui-audit.mjs +243 -30
@@ -1,5 +1,5 @@
1
1
  "use client";
2
- import { jsx, jsxs } from "react/jsx-runtime";
2
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
3
3
  import * as React from "react";
4
4
  import {
5
5
  columnFilteringFeature,
@@ -20,11 +20,13 @@ import {
20
20
  AlertCircle,
21
21
  ArrowDown,
22
22
  ArrowUp,
23
+ ChevronDown,
23
24
  ChevronLeft,
24
25
  ChevronRight,
25
26
  ChevronsUpDown,
26
27
  Layers,
27
28
  Layers2,
29
+ ListFilter,
28
30
  MoreHorizontal,
29
31
  RefreshCw,
30
32
  ShieldAlert,
@@ -47,10 +49,15 @@ import {
47
49
  DropdownMenu,
48
50
  DropdownMenuCheckboxItem,
49
51
  DropdownMenuContent,
52
+ DropdownMenuItem,
50
53
  DropdownMenuLabel,
54
+ DropdownMenuRadioGroup,
55
+ DropdownMenuRadioItem,
51
56
  DropdownMenuSeparator,
52
57
  DropdownMenuTrigger
53
58
  } from "../navigation/dropdown-menu.js";
59
+ import { RadioGroupRoot, RadioItem } from "../data-entry/radio.js";
60
+ import { Tooltip, TooltipContent, TooltipTrigger } from "../feedback/tooltip.js";
54
61
  import {
55
62
  Table,
56
63
  TableBody,
@@ -81,16 +88,81 @@ function dataTableFeatures() {
81
88
  columnMeta: {}
82
89
  });
83
90
  }
91
+ const DEFAULT_SORT_DIRECTIONS = ["asc", "desc"];
92
+ function columnIsSortable(col) {
93
+ if (col.sortable) return true;
94
+ if (col.sorter === true || typeof col.sorter === "function") return true;
95
+ return typeof col.sorter === "object" && col.sorter !== null;
96
+ }
97
+ function columnCompare(col) {
98
+ if (typeof col.sorter === "function") return col.sorter;
99
+ if (col.sorter && typeof col.sorter === "object") return col.sorter.compare;
100
+ return void 0;
101
+ }
102
+ function columnSortPriority(col) {
103
+ return col.sorter && typeof col.sorter === "object" ? col.sorter.multiple : void 0;
104
+ }
105
+ function columnSortDirections(col, tableDirections) {
106
+ const declared = col.sortDirections ?? tableDirections ?? DEFAULT_SORT_DIRECTIONS;
107
+ return declared.length > 0 ? declared : DEFAULT_SORT_DIRECTIONS;
108
+ }
109
+ function nextSortDirection(current, directions) {
110
+ if (!current) return directions[0];
111
+ const at = directions.indexOf(current);
112
+ if (at === -1) return directions[0];
113
+ return directions[at + 1];
114
+ }
115
+ function applySortToState(state, key, direction, priority, priorities) {
116
+ if (priority === void 0) {
117
+ return direction ? [{ id: key, desc: direction === "desc" }] : [];
118
+ }
119
+ const kept = state.filter((entry) => entry.id !== key && priorities[entry.id] !== void 0);
120
+ const next = direction ? [...kept, { id: key, desc: direction === "desc" }] : kept;
121
+ return next.sort((a, b) => (priorities[b.id] ?? 0) - (priorities[a.id] ?? 0));
122
+ }
123
+ function directionOf(state, key) {
124
+ const entry = state.find((s) => s.id === key);
125
+ return entry ? entry.desc ? "desc" : "asc" : void 0;
126
+ }
127
+ function columnFilterPredicate(col, row, values) {
128
+ if (values.length === 0) return true;
129
+ const onFilter = col.onFilter;
130
+ if (onFilter) return values.some((value) => onFilter(value, row));
131
+ const raw = row[col.key];
132
+ return values.some((value) => value === raw || String(value) === String(raw));
133
+ }
134
+ function useStableValue(value) {
135
+ const key = JSON.stringify(value);
136
+ const ref = React.useRef({ key, value });
137
+ if (ref.current.key !== key) ref.current = { key, value };
138
+ return ref.current.value;
139
+ }
140
+ function cssLength(value) {
141
+ if (value === void 0) return void 0;
142
+ return typeof value === "number" ? `${value}px` : value;
143
+ }
84
144
  function toTanstackColumns(columns) {
85
- return columns.map((col) => ({
86
- id: col.key,
87
- accessorFn: (row) => row[col.key],
88
- header: () => col.header,
89
- enableSorting: !!col.sortable,
90
- enableHiding: col.enableHiding ?? true,
91
- enableGlobalFilter: true,
92
- meta: { lean: col }
93
- }));
145
+ return columns.map((col) => {
146
+ const compare = columnCompare(col);
147
+ return {
148
+ id: col.key,
149
+ accessorFn: (row) => row[col.key],
150
+ header: () => col.header,
151
+ enableSorting: columnIsSortable(col),
152
+ enableHiding: col.enableHiding ?? true,
153
+ enableGlobalFilter: true,
154
+ enableColumnFilter: !!col.filters,
155
+ ...compare ? {
156
+ sortFn: (rowA, rowB) => compare(rowA.original, rowB.original)
157
+ } : {},
158
+ filterFn: (row, _id, filterValue) => columnFilterPredicate(
159
+ col,
160
+ row.original,
161
+ Array.isArray(filterValue) ? filterValue : []
162
+ ),
163
+ meta: { lean: col }
164
+ };
165
+ });
94
166
  }
95
167
  const DataTableContext = React.createContext(null);
96
168
  function useDataTableContext() {
@@ -128,7 +200,7 @@ function DataTable({
128
200
  onSortChange,
129
201
  globalFilter: controlledGlobalFilter,
130
202
  onGlobalFilterChange,
131
- pagination: controlledPagination,
203
+ pagination: paginationProp,
132
204
  onPaginationChange,
133
205
  rowCount,
134
206
  columnVisibility: controlledVisibility,
@@ -147,6 +219,16 @@ function DataTable({
147
219
  preset = "default",
148
220
  collapseBelow = "sm",
149
221
  rowClassName,
222
+ rowSelection,
223
+ expandable,
224
+ summary,
225
+ scroll,
226
+ sticky,
227
+ onRow,
228
+ bordered = false,
229
+ showSorterTooltip = false,
230
+ sortDirections,
231
+ onFilterChange,
150
232
  className,
151
233
  children
152
234
  }) {
@@ -156,23 +238,90 @@ function DataTable({
156
238
  setInternalDensity(d);
157
239
  onDensityChange?.(d);
158
240
  };
159
- const [internalSorting, setInternalSorting] = React.useState([]);
160
- const [internalFilters] = React.useState([]);
241
+ const pagerHidden = paginationProp === false;
242
+ const paginationConfig = paginationProp && typeof paginationProp === "object" && !("pageIndex" in paginationProp) ? paginationProp : void 0;
243
+ const controlledPagination = paginationProp && typeof paginationProp === "object" && "pageIndex" in paginationProp ? paginationProp : void 0;
244
+ const [internalSorting, setInternalSorting] = React.useState(
245
+ () => columns.filter((col) => col.defaultSortOrder).map((col) => ({ id: col.key, desc: col.defaultSortOrder === "desc" }))
246
+ );
247
+ const [internalFilters, setInternalFilters] = React.useState(
248
+ () => columns.filter((col) => (col.defaultFilteredValue?.length ?? 0) > 0).map((col) => ({ id: col.key, value: col.defaultFilteredValue }))
249
+ );
161
250
  const [internalGlobal, setInternalGlobal] = React.useState("");
162
251
  const [internalPagination, setInternalPagination] = React.useState({
163
252
  pageIndex: 0,
164
- pageSize: 10
253
+ pageSize: paginationConfig?.pageSize ?? 10
165
254
  });
166
- const [internalSelection, setInternalSelection] = React.useState({});
255
+ const [internalSelection, setInternalSelection] = React.useState(
256
+ () => Object.fromEntries(
257
+ (rowSelection?.defaultSelectedRowKeys ?? []).map((key) => [key, true])
258
+ )
259
+ );
167
260
  const [internalVisibility, setInternalVisibility] = React.useState({});
261
+ const [internalExpanded, setInternalExpanded] = React.useState(
262
+ () => expandable?.defaultExpandAllRows ? data.map(getRowId) : []
263
+ );
264
+ const pageIndex = controlledPagination?.pageIndex ?? (paginationConfig?.current !== void 0 ? Math.max(0, paginationConfig.current - 1) : internalPagination.pageIndex);
265
+ const pageSize = controlledPagination?.pageSize ?? paginationConfig?.pageSize ?? internalPagination.pageSize;
266
+ const resolvedPagination = React.useMemo(
267
+ () => ({ pageIndex, pageSize }),
268
+ [pageIndex, pageSize]
269
+ );
270
+ const handlePaginationChange = (updater) => {
271
+ const next = typeof updater === "function" ? updater(resolvedPagination) : updater;
272
+ setInternalPagination(next);
273
+ onPaginationChange?.(next);
274
+ paginationConfig?.onChange?.(next.pageIndex + 1, next.pageSize);
275
+ };
168
276
  const selectionFromSet = React.useCallback(
169
277
  (set) => Object.fromEntries([...set].map((id) => [id, true])),
170
278
  []
171
279
  );
172
- const sortingState = sort !== void 0 ? sortToSortingState(sort) : internalSorting;
173
- const rowSelection = controlledSelected !== void 0 ? selectionFromSet(controlledSelected) : internalSelection;
280
+ const sortPriorities = React.useMemo(() => {
281
+ const map = {};
282
+ for (const col of columns) {
283
+ const priority = columnSortPriority(col);
284
+ if (priority !== void 0) map[col.key] = priority;
285
+ }
286
+ return map;
287
+ }, [columns]);
288
+ const columnSortOrders = columns.filter((col) => col.sortOrder !== void 0);
289
+ const sortingState = useStableValue(
290
+ columnSortOrders.length > 0 ? columnSortOrders.filter((col) => col.sortOrder).map((col) => ({ id: col.key, desc: col.sortOrder === "desc" })).sort((a, b) => (sortPriorities[b.id] ?? 0) - (sortPriorities[a.id] ?? 0)) : sort !== void 0 ? sortToSortingState(sort) : internalSorting
291
+ );
292
+ const controlledFilterKeys = new Set(
293
+ columns.filter((col) => col.filteredValue !== void 0).map((col) => col.key)
294
+ );
295
+ const columnFilters = useStableValue([
296
+ ...internalFilters.filter((entry) => !controlledFilterKeys.has(entry.id)),
297
+ ...columns.filter((col) => (col.filteredValue?.length ?? 0) > 0).map((col) => ({ id: col.key, value: col.filteredValue }))
298
+ ]);
299
+ const filterValues = Object.fromEntries(
300
+ columnFilters.map((entry) => [entry.id, entry.value ?? []])
301
+ );
302
+ const setFilterValue = (key, next) => {
303
+ if (!controlledFilterKeys.has(key)) {
304
+ setInternalFilters((prev) => {
305
+ const rest = prev.filter((entry) => entry.id !== key);
306
+ return next.length > 0 ? [...rest, { id: key, value: next }] : rest;
307
+ });
308
+ }
309
+ const merged = { ...filterValues };
310
+ if (next.length > 0) {
311
+ merged[key] = next;
312
+ } else {
313
+ delete merged[key];
314
+ }
315
+ onFilterChange?.(merged);
316
+ };
317
+ const rowSelectionKeys = rowSelection?.selectedRowKeys;
318
+ const rowSelectionState = rowSelectionKeys !== void 0 ? Object.fromEntries(rowSelectionKeys.map((key) => [key, true])) : controlledSelected !== void 0 ? selectionFromSet(controlledSelected) : internalSelection;
174
319
  const onSortingChange = (updater) => {
175
320
  const next = typeof updater === "function" ? updater(sortingState) : updater;
321
+ if (columnSortOrders.length > 0) {
322
+ onSortChange?.(sortingStateToSort(next));
323
+ return;
324
+ }
176
325
  if (sort !== void 0 || onSortChange) {
177
326
  onSortChange?.(sortingStateToSort(next));
178
327
  } else {
@@ -180,14 +329,38 @@ function DataTable({
180
329
  }
181
330
  };
182
331
  const onRowSelectionChange = (updater) => {
183
- const next = typeof updater === "function" ? updater(rowSelection) : updater;
184
- if (controlledSelected !== void 0 || onSelectChange) {
185
- const set = new Set(Object.keys(next).filter((id) => next[id]));
186
- onSelectChange?.(set);
187
- if (controlledSelected === void 0) setInternalSelection(next);
188
- } else {
332
+ let next = typeof updater === "function" ? updater(rowSelectionState) : updater;
333
+ if (rowSelection?.type === "radio") {
334
+ const added = Object.keys(next).filter((key) => next[key] && !rowSelectionState[key]);
335
+ const only = added[added.length - 1];
336
+ next = only ? { [only]: true } : {};
337
+ }
338
+ if (rowSelection) {
339
+ const onPage = new Set(data.map(getRowId));
340
+ if (rowSelection.preserveSelectedRowKeys) {
341
+ const offPage = Object.keys(rowSelectionState).filter(
342
+ (key) => rowSelectionState[key] && !onPage.has(key)
343
+ );
344
+ next = {
345
+ ...next,
346
+ ...Object.fromEntries(offPage.map((key) => [key, true]))
347
+ };
348
+ } else {
349
+ next = Object.fromEntries(
350
+ Object.entries(next).filter(([key, on]) => on && onPage.has(key))
351
+ );
352
+ }
353
+ }
354
+ const keys = Object.keys(next).filter((key) => next[key]);
355
+ if (rowSelectionKeys === void 0 && controlledSelected === void 0) {
189
356
  setInternalSelection(next);
190
357
  }
358
+ onSelectChange?.(new Set(keys));
359
+ const byId = new Map(data.map((row) => [getRowId(row), row]));
360
+ rowSelection?.onChange?.(
361
+ keys,
362
+ keys.map((key) => byId.get(key)).filter((row) => row !== void 0)
363
+ );
191
364
  };
192
365
  const onGlobalFilterChangeFn = (updater) => {
193
366
  const current = controlledGlobalFilter ?? internalGlobal;
@@ -203,8 +376,9 @@ function DataTable({
203
376
  const hasNumberedPager = React.Children.toArray(children).some(
204
377
  (c) => React.isValidElement(c) && c.type.displayName === "DataTable.Pagination" && typeof c.props.onChange !== "function"
205
378
  );
206
- const paginationEngaged = manualPagination || controlledPagination !== void 0 || onPaginationChange !== void 0 || hasNumberedPager;
379
+ const paginationEngaged = !pagerHidden && (manualPagination || controlledPagination !== void 0 || paginationConfig !== void 0 || onPaginationChange !== void 0 || hasNumberedPager);
207
380
  const features = React.useMemo(() => dataTableFeatures(), []);
381
+ const selectionEnabled = selectable || rowSelection !== void 0;
208
382
  const table = useTable({
209
383
  features,
210
384
  data,
@@ -215,27 +389,33 @@ function DataTable({
215
389
  // "no pager on this table" is expressed the same way as "the server paginates" — leave the
216
390
  // rows unsliced. Page count still derives from `rowCount`/the pre-paginated model either way.
217
391
  manualPagination: manualPagination || !paginationEngaged,
218
- rowCount,
219
- enableRowSelection: selectable,
392
+ rowCount: paginationConfig?.total ?? rowCount,
393
+ enableRowSelection: selectionEnabled,
220
394
  state: {
221
395
  sorting: sortingState,
222
- columnFilters: internalFilters,
396
+ columnFilters,
223
397
  globalFilter: controlledGlobalFilter ?? internalGlobal,
224
- pagination: controlledPagination ?? internalPagination,
225
- rowSelection,
398
+ pagination: resolvedPagination,
399
+ rowSelection: rowSelectionState,
226
400
  columnVisibility: controlledVisibility ?? internalVisibility
227
401
  },
228
402
  onSortingChange,
229
403
  onGlobalFilterChange: onGlobalFilterChangeFn,
230
- onPaginationChange: onPaginationChange ?? setInternalPagination,
404
+ onPaginationChange: handlePaginationChange,
231
405
  onRowSelectionChange,
232
406
  onColumnVisibilityChange: onColumnVisibilityChange ?? setInternalVisibility
233
407
  });
408
+ const expandedKeys = expandable?.expandedRowKeys ?? internalExpanded;
409
+ const toggleExpanded = (key) => {
410
+ const next = expandedKeys.includes(key) ? expandedKeys.filter((k) => k !== key) : [...expandedKeys, key];
411
+ if (expandable?.expandedRowKeys === void 0) setInternalExpanded(next);
412
+ expandable?.onExpandedRowsChange?.(next);
413
+ };
234
414
  const ctx = {
235
415
  table,
236
416
  density,
237
417
  setDensity,
238
- selectable,
418
+ selectable: selectionEnabled,
239
419
  sort,
240
420
  onSortChange,
241
421
  onRowClick,
@@ -246,10 +426,28 @@ function DataTable({
246
426
  onRetry,
247
427
  striped,
248
428
  hoverable,
249
- stickyHeader,
429
+ // antd `sticky` supersedes `stickyHeader` whenever it is given (either form of it).
430
+ stickyHeader: sticky === void 0 ? stickyHeader : sticky !== false,
250
431
  preset,
251
432
  collapseBelow,
252
- rowClassName
433
+ rowClassName,
434
+ bordered,
435
+ scroll,
436
+ sticky,
437
+ onRow,
438
+ summary,
439
+ expandable,
440
+ expandedKeys,
441
+ toggleExpanded,
442
+ rowSelection,
443
+ getRowId,
444
+ showSorterTooltip,
445
+ sortDirections,
446
+ sortPriorities,
447
+ filterValues,
448
+ setFilterValue,
449
+ paginationConfig,
450
+ pagerHidden
253
451
  };
254
452
  const hasContent = React.Children.toArray(children).some(
255
453
  (c) => React.isValidElement(c) && c.type.displayName === "DataTable.Content"
@@ -342,12 +540,15 @@ function columnLabel(column) {
342
540
  return column.id;
343
541
  }
344
542
  DataTable.SelectAll = function DataTableSelectAll() {
345
- const { table, selectable } = useDataTableContext();
543
+ const { table, selectable, rowSelection, getRowId } = useDataTableContext();
346
544
  const { t } = useTranslation();
347
545
  if (!selectable) return null;
546
+ if (rowSelection?.type === "radio" || rowSelection?.hideSelectAll) {
547
+ return /* @__PURE__ */ jsx(Fragment, { children: rowSelection.columnTitle ?? /* @__PURE__ */ jsx("span", { className: "sr-only", children: t("dataTable.selectColumn") }) });
548
+ }
348
549
  const allSelected = table.getIsAllPageRowsSelected();
349
550
  const someSelected = table.getIsSomePageRowsSelected();
350
- return /* @__PURE__ */ jsx(
551
+ const box = /* @__PURE__ */ jsx(
351
552
  Checkbox,
352
553
  {
353
554
  checked: allSelected ? true : someSelected ? "indeterminate" : false,
@@ -355,6 +556,54 @@ DataTable.SelectAll = function DataTableSelectAll() {
355
556
  "aria-label": t("dataTable.selectAll")
356
557
  }
357
558
  );
559
+ if (rowSelection?.columnTitle !== void 0) return /* @__PURE__ */ jsx(Fragment, { children: rowSelection.columnTitle });
560
+ if (!rowSelection?.selections) return box;
561
+ const pageKeys = table.getRowModel().rows.map((row) => getRowId(row.original));
562
+ const selectedKeys = table.getSelectedRowModel().rows.map((row) => getRowId(row.original));
563
+ const builtIn = [
564
+ {
565
+ key: "all",
566
+ text: t("dataTable.selectAll"),
567
+ onSelect: () => table.toggleAllPageRowsSelected(true)
568
+ },
569
+ {
570
+ key: "invert",
571
+ text: t("dataTable.selectInvert"),
572
+ onSelect: () => {
573
+ table.setRowSelection(
574
+ Object.fromEntries(
575
+ table.getRowModel().rows.filter((row) => !row.getIsSelected()).map((row) => [getRowId(row.original), true])
576
+ )
577
+ );
578
+ }
579
+ },
580
+ {
581
+ key: "none",
582
+ text: t("dataTable.selectNone"),
583
+ onSelect: () => table.toggleAllPageRowsSelected(false)
584
+ }
585
+ ];
586
+ const entries = rowSelection.selections === true ? builtIn : rowSelection.selections.map((entry) => ({
587
+ key: entry.key,
588
+ text: entry.text,
589
+ onSelect: () => entry.onSelect(pageKeys.length > 0 ? pageKeys : selectedKeys)
590
+ }));
591
+ return /* @__PURE__ */ jsxs("span", { className: "ui-data-table-selection-menu", children: [
592
+ box,
593
+ /* @__PURE__ */ jsxs(DropdownMenu, { children: [
594
+ /* @__PURE__ */ jsx(DropdownMenuTrigger, { asChild: true, children: /* @__PURE__ */ jsx(
595
+ Button,
596
+ {
597
+ variant: "ghost",
598
+ size: "icon-xs",
599
+ "aria-label": t("dataTable.selectionMenu"),
600
+ className: "ui-data-table-selection-trigger",
601
+ children: /* @__PURE__ */ jsx(ChevronDown, { className: "ui-data-table-sort-icon", "aria-hidden": "true" })
602
+ }
603
+ ) }),
604
+ /* @__PURE__ */ jsx(DropdownMenuContent, { align: "start", children: entries.map((entry) => /* @__PURE__ */ jsx(DropdownMenuItem, { onSelect: entry.onSelect, children: entry.text }, entry.key)) })
605
+ ] })
606
+ ] });
358
607
  };
359
608
  DataTable.SelectAll.displayName = "DataTable.SelectAll";
360
609
  DataTable.BulkActions = function DataTableBulkActions({
@@ -418,12 +667,78 @@ DataTable.DensityToggle = function DataTableDensityToggle() {
418
667
  );
419
668
  };
420
669
  DataTable.DensityToggle.displayName = "DataTable.DensityToggle";
670
+ function fixedEdge(col) {
671
+ return col.fixed ?? (col.pin === "end" ? "end" : void 0);
672
+ }
673
+ function sameOffsets(a, b) {
674
+ const keys = Object.keys(a);
675
+ if (keys.length !== Object.keys(b).length) return false;
676
+ return keys.every((key) => a[key] === b[key]);
677
+ }
678
+ function ColumnFilterMenu({ column }) {
679
+ const { filterValues, setFilterValue } = useDataTableContext();
680
+ const { t } = useTranslation();
681
+ const options = column.filters ?? [];
682
+ const active = filterValues[column.key] ?? [];
683
+ const multiple = column.filterMultiple ?? true;
684
+ const apply = (next) => {
685
+ setFilterValue(column.key, next);
686
+ };
687
+ return /* @__PURE__ */ jsxs(DropdownMenu, { children: [
688
+ /* @__PURE__ */ jsx(DropdownMenuTrigger, { asChild: true, children: /* @__PURE__ */ jsx(
689
+ Button,
690
+ {
691
+ variant: "ghost",
692
+ size: "icon-xs",
693
+ "aria-label": t("dataTable.filterColumn"),
694
+ "data-filtered": active.length > 0 ? "" : void 0,
695
+ className: "ui-data-table-filter-trigger",
696
+ children: /* @__PURE__ */ jsx(ListFilter, { className: "ui-data-table-sort-icon", "aria-hidden": "true" })
697
+ }
698
+ ) }),
699
+ /* @__PURE__ */ jsxs(DropdownMenuContent, { align: "start", children: [
700
+ /* @__PURE__ */ jsx(DropdownMenuLabel, { children: t("dataTable.filterColumn") }),
701
+ /* @__PURE__ */ jsx(DropdownMenuSeparator, {}),
702
+ multiple ? options.map((option) => /* @__PURE__ */ jsx(
703
+ DropdownMenuCheckboxItem,
704
+ {
705
+ checked: active.includes(option.value),
706
+ onCheckedChange: (checked) => {
707
+ apply(
708
+ checked ? [...active, option.value] : active.filter((value) => value !== option.value)
709
+ );
710
+ },
711
+ children: option.text
712
+ },
713
+ String(option.value)
714
+ )) : /* @__PURE__ */ jsx(
715
+ DropdownMenuRadioGroup,
716
+ {
717
+ value: active.length > 0 ? String(active[0]) : "",
718
+ onValueChange: (value) => {
719
+ const picked = options.find((option) => String(option.value) === value);
720
+ apply(picked ? [picked.value] : []);
721
+ },
722
+ children: options.map((option) => /* @__PURE__ */ jsx(DropdownMenuRadioItem, { value: String(option.value), children: option.text }, String(option.value)))
723
+ }
724
+ ),
725
+ /* @__PURE__ */ jsx(DropdownMenuSeparator, {}),
726
+ /* @__PURE__ */ jsx(
727
+ DropdownMenuItem,
728
+ {
729
+ onSelect: () => {
730
+ apply([]);
731
+ },
732
+ children: t("dataTable.filterReset")
733
+ }
734
+ )
735
+ ] })
736
+ ] });
737
+ }
421
738
  DataTable.Content = function DataTableContent() {
422
739
  const {
423
740
  table,
424
741
  selectable,
425
- sort,
426
- onSortChange,
427
742
  onRowClick,
428
743
  loading,
429
744
  empty,
@@ -435,7 +750,20 @@ DataTable.Content = function DataTableContent() {
435
750
  stickyHeader,
436
751
  preset,
437
752
  collapseBelow,
438
- rowClassName
753
+ rowClassName,
754
+ bordered,
755
+ scroll,
756
+ sticky,
757
+ onRow,
758
+ summary,
759
+ expandable,
760
+ expandedKeys,
761
+ toggleExpanded,
762
+ rowSelection,
763
+ getRowId,
764
+ showSorterTooltip,
765
+ sortDirections,
766
+ sortPriorities
439
767
  } = useDataTableContext();
440
768
  const { t } = useTranslation();
441
769
  const presetAttr = preset === "default" ? void 0 : preset;
@@ -444,8 +772,13 @@ DataTable.Content = function DataTableContent() {
444
772
  const rowPadding = tableRowHeightClass;
445
773
  const cellPadding = tableCellPaddingClass;
446
774
  const visibleColumns = table.getVisibleLeafColumns().map((c) => c.columnDef.meta?.lean).filter((c) => !!c);
447
- const emptyColSpan = visibleColumns.length + (selectable ? 1 : 0);
448
- const hasPinEnd = visibleColumns.some((col) => col.pin === "end");
775
+ const expandColumnShown = !!expandable?.expandedRowRender;
776
+ const leadingColumnCount = (expandColumnShown ? 1 : 0) + (selectable ? 1 : 0);
777
+ const emptyColSpan = visibleColumns.length + leadingColumnCount;
778
+ const hasPinEnd = visibleColumns.some((col) => fixedEdge(col) === "end");
779
+ const hasFixedStart = visibleColumns.some((col) => fixedEdge(col) === "start");
780
+ const leadingFixed = hasFixedStart ? "start" : void 0;
781
+ const fixedTableLayout = scroll?.x !== void 0 || visibleColumns.some((col) => col.ellipsis || columnWidth(col.width).style !== void 0);
449
782
  const missingHeaderNames = visibleColumns.filter((col) => isEmptyHeader(col.header) && !col.ariaLabel).map((col) => col.key).join("|");
450
783
  React.useEffect(() => {
451
784
  if (process.env.NODE_ENV === "production" || !missingHeaderNames) return;
@@ -475,24 +808,82 @@ DataTable.Content = function DataTableContent() {
475
808
  observer.disconnect();
476
809
  };
477
810
  }, []);
478
- const activeSort = sort ?? sortingStateToSort(table.state.sorting);
479
- const isControlledSort = sort !== void 0 || !!onSortChange;
480
- const onHeaderClick = (col) => {
481
- if (!col.sortable) return;
482
- if (isControlledSort) {
483
- if (activeSort?.key !== col.key) {
484
- onSortChange?.({ key: col.key, direction: "asc" });
485
- } else if (activeSort.direction === "asc") {
486
- onSortChange?.({ key: col.key, direction: "desc" });
487
- } else {
488
- onSortChange?.(void 0);
811
+ const [fixedOffsets, setFixedOffsets] = React.useState({});
812
+ const fixedSignature = [
813
+ leadingFixed ?? "",
814
+ expandColumnShown ? "x" : "",
815
+ selectable ? "s" : "",
816
+ ...visibleColumns.map((col) => `${col.key}:${fixedEdge(col) ?? ""}`)
817
+ ].join("|");
818
+ React.useEffect(() => {
819
+ const el = scrollRef.current;
820
+ if (!el) return;
821
+ const measure = () => {
822
+ const headerRow2 = el.querySelector("thead tr");
823
+ if (!headerRow2) return;
824
+ const cells = Array.from(headerRow2.children);
825
+ const next = {};
826
+ let start = 0;
827
+ for (const cell of cells) {
828
+ if (cell.dataset.fixed !== "start") continue;
829
+ next[cell.dataset.columnKey ?? ""] = start;
830
+ start += cell.offsetWidth;
489
831
  }
490
- return;
491
- }
492
- table.getColumn(col.key)?.toggleSorting();
832
+ let end = 0;
833
+ for (const cell of [...cells].reverse()) {
834
+ if (cell.dataset.fixed !== "end") continue;
835
+ next[cell.dataset.columnKey ?? ""] = end;
836
+ end += cell.offsetWidth;
837
+ }
838
+ setFixedOffsets((prev) => sameOffsets(prev, next) ? prev : next);
839
+ };
840
+ measure();
841
+ const observer = new ResizeObserver(measure);
842
+ observer.observe(el);
843
+ const headerRow = el.querySelector("thead tr");
844
+ if (headerRow) observer.observe(headerRow);
845
+ return () => {
846
+ observer.disconnect();
847
+ };
848
+ }, [fixedSignature]);
849
+ const fixedCellProps = (key, edge) => edge ? { "data-column-key": key, "data-fixed": edge } : { "data-column-key": key };
850
+ const leadingFixedStyle = (key) => leadingFixed ? { "--table-fixed-offset": fixedOffsets[key] ?? 0 } : void 0;
851
+ const columnCellClass = (col) => {
852
+ const edge = fixedEdge(col);
853
+ return cn(
854
+ columnWidth(col.width).className,
855
+ col.align === "right" && "text-end",
856
+ col.align === "center" && "text-center",
857
+ col.hiddenOnMobile && "hidden md:table-cell",
858
+ col.ellipsis && "ui-data-table-ellipsis",
859
+ edge === "end" && "ui-data-table-pin-end",
860
+ edge === "start" && "ui-data-table-pin-start"
861
+ );
862
+ };
863
+ const columnCellStyle = (col) => {
864
+ const base = columnWidth(col.width).style;
865
+ const edge = fixedEdge(col);
866
+ if (!edge) return base;
867
+ return { ...base, "--table-fixed-offset": fixedOffsets[col.key] ?? 0 };
868
+ };
869
+ const sortingState = table.state.sorting;
870
+ const onHeaderClick = (col) => {
871
+ if (!columnIsSortable(col)) return;
872
+ const directions = columnSortDirections(col, sortDirections);
873
+ const next = nextSortDirection(directionOf(sortingState, col.key), directions);
874
+ table.setSorting(
875
+ applySortToState(sortingState, col.key, next, sortPriorities[col.key], sortPriorities)
876
+ );
493
877
  };
494
878
  const dataRows = table.getRowModel().rows;
495
879
  const rowCount = dataRows.length;
880
+ const scrollStyle = {};
881
+ const scrollVars = scrollStyle;
882
+ if (scroll?.x !== void 0) scrollVars["--table-scroll-inline-size"] = cssLength(scroll.x);
883
+ if (scroll?.y !== void 0) scrollVars["--table-scroll-block-size"] = cssLength(scroll.y);
884
+ if (typeof sticky === "object" && sticky.offsetHeader !== void 0) {
885
+ scrollVars["--table-sticky-offset"] = cssLength(sticky.offsetHeader);
886
+ }
496
887
  return /* @__PURE__ */ jsx(
497
888
  "div",
498
889
  {
@@ -502,6 +893,9 @@ DataTable.Content = function DataTableContent() {
502
893
  hasPinEnd && "ui-data-table-has-pin-end",
503
894
  hasOverflowEnd && "ui-data-table-has-overflow-end"
504
895
  ),
896
+ style: scrollStyle,
897
+ "data-scroll-x": scroll?.x !== void 0 ? "" : void 0,
898
+ "data-scroll-y": scroll?.y !== void 0 ? "" : void 0,
505
899
  "aria-busy": loading,
506
900
  tabIndex: presetAttr ? void 0 : 0,
507
901
  children: /* @__PURE__ */ jsx(
@@ -509,52 +903,69 @@ DataTable.Content = function DataTableContent() {
509
903
  {
510
904
  className: "ui-data-table-surface",
511
905
  "data-preset": presetAttr,
906
+ "data-table-layout": fixedTableLayout ? "fixed" : void 0,
512
907
  "data-striped": striped ? "" : void 0,
513
908
  "data-hoverable": hoverable ? "" : void 0,
514
- children: /* @__PURE__ */ jsxs(Table, { scrollable: preset !== "default", preset, collapseBelow, children: [
515
- /* @__PURE__ */ jsx(
516
- TableHeader,
517
- {
518
- className: cn("bg-secondary", stickyHeader && "ui-data-table-sticky-header"),
519
- children: /* @__PURE__ */ jsxs(TableRow, { children: [
520
- selectable && /* @__PURE__ */ jsx(TableHead, { className: "ui-data-table-select-column", children: /* @__PURE__ */ jsx(DataTable.SelectAll, {}) }),
521
- visibleColumns.map((col) => {
522
- const isSortable = !!col.sortable;
523
- const isActiveSort = isSortable && activeSort?.key === col.key;
524
- const sortIndicator = isSortable ? isActiveSort ? activeSort?.direction === "asc" ? /* @__PURE__ */ jsx(ArrowUp, { className: "ui-data-table-sort-icon", "aria-hidden": "true" }) : /* @__PURE__ */ jsx(ArrowDown, { className: "ui-data-table-sort-icon", "aria-hidden": "true" }) : /* @__PURE__ */ jsx(
525
- ChevronsUpDown,
526
- {
527
- className: "ui-data-table-sort-icon text-muted-foreground",
528
- "aria-hidden": "true"
529
- }
530
- ) : null;
531
- const headerEmpty = isEmptyHeader(col.header);
532
- const headerContent = headerEmpty && col.ariaLabel ? /* @__PURE__ */ jsx("span", { className: "sr-only", children: col.ariaLabel }) : col.header;
533
- const label = /* @__PURE__ */ jsxs("span", { className: "ui-data-table-sort-label", children: [
534
- headerContent,
535
- sortIndicator
536
- ] });
537
- return /* @__PURE__ */ jsx(
538
- TableHead,
539
- {
540
- priority: col.priority,
541
- "data-empty": headerEmpty || void 0,
542
- "data-align": col.headerAlign ?? col.align,
543
- "aria-sort": isSortable ? isActiveSort ? activeSort?.direction === "asc" ? "ascending" : "descending" : "none" : void 0,
544
- style: columnWidth(col.width).style,
545
- className: cn(
546
- columnWidth(col.width).className,
547
- // `headerAlign` when the heading differs from the rows,
548
- // `align` otherwise. A table wanting centred headings
549
- // over start-aligned text could not say so before, and
550
- // consumers reached for `[&_th_button]:justify-center`.
551
- (col.headerAlign ?? col.align) === "right" && "text-end",
552
- (col.headerAlign ?? col.align) === "center" && "text-center",
553
- col.hiddenOnMobile && "hidden md:table-cell",
554
- isSortable && "select-none",
555
- col.pin === "end" && "ui-data-table-pin-end"
556
- ),
557
- children: isSortable ? /* @__PURE__ */ jsx(
909
+ children: /* @__PURE__ */ jsxs(
910
+ Table,
911
+ {
912
+ scrollable: preset !== "default",
913
+ preset,
914
+ collapseBelow,
915
+ bordered,
916
+ children: [
917
+ /* @__PURE__ */ jsx(
918
+ TableHeader,
919
+ {
920
+ className: cn("bg-secondary", stickyHeader && "ui-data-table-sticky-header"),
921
+ children: /* @__PURE__ */ jsxs(TableRow, { children: [
922
+ expandColumnShown && /* @__PURE__ */ jsx(
923
+ TableHead,
924
+ {
925
+ className: cn(
926
+ "ui-data-table-expand-column",
927
+ leadingFixed === "start" && "ui-data-table-pin-start"
928
+ ),
929
+ ...fixedCellProps("__expand", leadingFixed),
930
+ style: leadingFixedStyle("__expand"),
931
+ children: /* @__PURE__ */ jsx("span", { className: "sr-only", children: expandable?.columnTitle ?? t("dataTable.expandColumn") })
932
+ }
933
+ ),
934
+ selectable && /* @__PURE__ */ jsx(
935
+ TableHead,
936
+ {
937
+ className: cn(
938
+ "ui-data-table-select-column",
939
+ leadingFixed === "start" && "ui-data-table-pin-start"
940
+ ),
941
+ ...fixedCellProps("__select", leadingFixed),
942
+ style: leadingFixedStyle("__select"),
943
+ children: /* @__PURE__ */ jsx(DataTable.SelectAll, {})
944
+ }
945
+ ),
946
+ visibleColumns.map((col) => {
947
+ const isSortable = columnIsSortable(col);
948
+ const activeDirection = directionOf(sortingState, col.key);
949
+ const sortIndicator = isSortable ? activeDirection ? activeDirection === "asc" ? /* @__PURE__ */ jsx(ArrowUp, { className: "ui-data-table-sort-icon", "aria-hidden": "true" }) : /* @__PURE__ */ jsx(ArrowDown, { className: "ui-data-table-sort-icon", "aria-hidden": "true" }) : /* @__PURE__ */ jsx(
950
+ ChevronsUpDown,
951
+ {
952
+ className: "ui-data-table-sort-icon text-muted-foreground",
953
+ "aria-hidden": "true"
954
+ }
955
+ ) : null;
956
+ const headerEmpty = isEmptyHeader(col.header);
957
+ const headerContent = headerEmpty && col.ariaLabel ? /* @__PURE__ */ jsx("span", { className: "sr-only", children: col.ariaLabel }) : col.header;
958
+ const label = /* @__PURE__ */ jsxs("span", { className: "ui-data-table-sort-label", children: [
959
+ headerContent,
960
+ sortIndicator
961
+ ] });
962
+ const tooltipOn = col.showSorterTooltip ?? showSorterTooltip;
963
+ const nextDirection = nextSortDirection(
964
+ activeDirection,
965
+ columnSortDirections(col, sortDirections)
966
+ );
967
+ const sortHint = nextDirection === "asc" ? t("dataTable.sortAscending") : nextDirection === "desc" ? t("dataTable.sortDescending") : t("dataTable.sortCancel");
968
+ const sortButton = /* @__PURE__ */ jsx(
558
969
  "button",
559
970
  {
560
971
  type: "button",
@@ -564,179 +975,300 @@ DataTable.Content = function DataTableContent() {
564
975
  },
565
976
  children: label
566
977
  }
567
- ) : label
568
- },
569
- col.key
570
- );
571
- })
572
- ] })
573
- }
574
- ),
575
- /* @__PURE__ */ jsx(TableBody, { children: loading ? (
576
- // Shaped skeleton rows rendered INSIDE the real table grid so they
577
- // share its borders + column widths — no second framed container
578
- // (which double-borders when the table sits in a Card). Count is
579
- // bounded to the previous page so the height barely shifts.
580
- Array.from({ length: Math.min(Math.max(rowCount, 6), 10) }).map((_, i) => /* @__PURE__ */ jsxs(TableRow, { className: cn(rowPadding, "hover:bg-transparent"), children: [
581
- selectable && /* @__PURE__ */ jsx(TableCell, { className: cellPadding, children: /* @__PURE__ */ jsx("div", { className: "ui-skeleton-block ui-data-table-skeleton-check" }) }),
582
- visibleColumns.map((col, j) => /* @__PURE__ */ jsx(
583
- TableCell,
584
- {
585
- priority: col.priority,
586
- "data-align": col.align,
587
- style: columnWidth(col.width).style,
588
- className: cn(
589
- cellPadding,
590
- columnWidth(col.width).className,
591
- col.align === "right" && "text-end",
592
- col.align === "center" && "text-center",
593
- col.hiddenOnMobile && "hidden md:table-cell",
594
- col.pin === "end" && "ui-data-table-pin-end"
595
- ),
596
- children: /* @__PURE__ */ jsx(
597
- "div",
978
+ );
979
+ return /* @__PURE__ */ jsxs(
980
+ TableHead,
981
+ {
982
+ priority: col.priority,
983
+ "data-empty": headerEmpty || void 0,
984
+ "data-align": col.headerAlign ?? col.align,
985
+ "aria-sort": isSortable ? activeDirection ? activeDirection === "asc" ? "ascending" : "descending" : "none" : void 0,
986
+ ...fixedCellProps(col.key, fixedEdge(col)),
987
+ style: columnCellStyle(col),
988
+ className: cn(
989
+ columnCellClass(col),
990
+ // `headerAlign` when the heading differs from the rows,
991
+ // `align` otherwise. A table wanting centred headings
992
+ // over start-aligned text could not say so before, and
993
+ // consumers reached for `[&_th_button]:justify-center`.
994
+ (col.headerAlign ?? col.align) === "right" && "text-end",
995
+ (col.headerAlign ?? col.align) === "center" && "text-center",
996
+ isSortable && "select-none"
997
+ ),
998
+ children: [
999
+ isSortable ? tooltipOn ? /* @__PURE__ */ jsxs(Tooltip, { children: [
1000
+ /* @__PURE__ */ jsx(TooltipTrigger, { asChild: true, children: sortButton }),
1001
+ /* @__PURE__ */ jsx(TooltipContent, { children: sortHint })
1002
+ ] }) : sortButton : label,
1003
+ col.filters ? /* @__PURE__ */ jsx(ColumnFilterMenu, { column: col }) : null
1004
+ ]
1005
+ },
1006
+ col.key
1007
+ );
1008
+ })
1009
+ ] })
1010
+ }
1011
+ ),
1012
+ /* @__PURE__ */ jsx(TableBody, { children: loading ? (
1013
+ // Shaped skeleton rows rendered INSIDE the real table grid so they
1014
+ // share its borders + column widths — no second framed container
1015
+ // (which double-borders when the table sits in a Card). Count is
1016
+ // bounded to the previous page so the height barely shifts.
1017
+ Array.from({ length: Math.min(Math.max(rowCount, 6), 10) }).map((_, i) => /* @__PURE__ */ jsxs(TableRow, { className: cn(rowPadding, "hover:bg-transparent"), children: [
1018
+ expandColumnShown && /* @__PURE__ */ jsx(TableCell, { className: cellPadding }),
1019
+ selectable && /* @__PURE__ */ jsx(TableCell, { className: cellPadding, children: /* @__PURE__ */ jsx("div", { className: "ui-skeleton-block ui-data-table-skeleton-check" }) }),
1020
+ visibleColumns.map((col, j) => /* @__PURE__ */ jsx(
1021
+ TableCell,
598
1022
  {
599
- className: cn(
600
- "ui-skeleton-block ui-data-table-skeleton-line",
601
- j === 0 ? "w-1/2" : "w-3/4",
602
- col.align === "right" && "ms-auto",
603
- col.align === "center" && "mx-auto"
1023
+ priority: col.priority,
1024
+ "data-align": col.align,
1025
+ style: columnCellStyle(col),
1026
+ className: cn(cellPadding, columnCellClass(col)),
1027
+ children: /* @__PURE__ */ jsx(
1028
+ "div",
1029
+ {
1030
+ className: cn(
1031
+ "ui-skeleton-block ui-data-table-skeleton-line",
1032
+ j === 0 ? "w-1/2" : "w-3/4",
1033
+ col.align === "right" && "ms-auto",
1034
+ col.align === "center" && "mx-auto"
1035
+ )
1036
+ }
604
1037
  )
605
- }
606
- )
607
- },
608
- col.key
609
- ))
610
- ] }, `skeleton-${i}`))
611
- ) : isDenied ? (
612
- // repeating a 403 cannot succeed. `warning` (not destructive) keeps it a policy
613
- // statement rather than a system fault. Announced politely: a permission boundary
614
- // is expected information, not an interruption.
615
- /* @__PURE__ */ jsx(TableRow, { className: "hover:bg-transparent", children: /* @__PURE__ */ jsx(
616
- TableCell,
617
- {
618
- colSpan: emptyColSpan,
619
- className: "ui-data-table-empty",
620
- "aria-live": "polite",
621
- children: denied === true ? /* @__PURE__ */ jsx(
1038
+ },
1039
+ col.key
1040
+ ))
1041
+ ] }, `skeleton-${i}`))
1042
+ ) : isDenied ? (
1043
+ // repeating a 403 cannot succeed. `warning` (not destructive) keeps it a policy
1044
+ // statement rather than a system fault. Announced politely: a permission boundary
1045
+ // is expected information, not an interruption.
1046
+ /* @__PURE__ */ jsx(TableRow, { className: "hover:bg-transparent", children: /* @__PURE__ */ jsx(
1047
+ TableCell,
1048
+ {
1049
+ colSpan: emptyColSpan,
1050
+ className: "ui-data-table-empty",
1051
+ "aria-live": "polite",
1052
+ children: denied === true ? /* @__PURE__ */ jsx(
1053
+ EmptyState,
1054
+ {
1055
+ icon: ShieldAlert,
1056
+ tone: "warning",
1057
+ title: t("dataTable.denied"),
1058
+ description: t("dataTable.deniedDescription"),
1059
+ titleAs: "p"
1060
+ }
1061
+ ) : denied
1062
+ }
1063
+ ) })
1064
+ ) : isError ? (
1065
+ // Announced assertively (`role="alert"`) because the
1066
+ // user's request did not complete — unlike empty/denied, which are states of the data
1067
+ // itself. The role sits on an inner wrapper, never on the <td>: overriding a cell's
1068
+ // role would strip it out of the table's grid semantics for AT.
1069
+ /* @__PURE__ */ jsx(TableRow, { className: "hover:bg-transparent", children: /* @__PURE__ */ jsx(TableCell, { colSpan: emptyColSpan, className: "ui-data-table-empty", children: /* @__PURE__ */ jsx("div", { role: "alert", children: error === true ? /* @__PURE__ */ jsx(
622
1070
  EmptyState,
623
1071
  {
624
- icon: ShieldAlert,
625
- tone: "warning",
626
- title: t("dataTable.denied"),
627
- description: t("dataTable.deniedDescription"),
628
- titleAs: "p"
1072
+ icon: AlertCircle,
1073
+ tone: "destructive",
1074
+ title: t("dataTable.error"),
1075
+ description: t("dataTable.errorDescription"),
1076
+ titleAs: "p",
1077
+ action: onRetry ? /* @__PURE__ */ jsxs(Button, { variant: "outline", size: "sm", onClick: onRetry, children: [
1078
+ /* @__PURE__ */ jsx(RefreshCw, { "aria-hidden": "true" }),
1079
+ t("common.retry")
1080
+ ] }) : void 0
629
1081
  }
630
- ) : denied
631
- }
632
- ) })
633
- ) : isError ? (
634
- // Announced assertively (`role="alert"`) because the
635
- // user's request did not complete — unlike empty/denied, which are states of the data
636
- // itself. The role sits on an inner wrapper, never on the <td>: overriding a cell's
637
- // role would strip it out of the table's grid semantics for AT.
638
- /* @__PURE__ */ jsx(TableRow, { className: "hover:bg-transparent", children: /* @__PURE__ */ jsx(TableCell, { colSpan: emptyColSpan, className: "ui-data-table-empty", children: /* @__PURE__ */ jsx("div", { role: "alert", children: error === true ? /* @__PURE__ */ jsx(
639
- EmptyState,
640
- {
641
- icon: AlertCircle,
642
- tone: "destructive",
643
- title: t("dataTable.error"),
644
- description: t("dataTable.errorDescription"),
645
- titleAs: "p",
646
- action: onRetry ? /* @__PURE__ */ jsxs(Button, { variant: "outline", size: "sm", onClick: onRetry, children: [
647
- /* @__PURE__ */ jsx(RefreshCw, { "aria-hidden": "true" }),
648
- t("common.retry")
649
- ] }) : void 0
650
- }
651
- ) : error }) }) })
652
- ) : rowCount === 0 ? /* @__PURE__ */ jsx(TableRow, { className: "hover:bg-transparent", children: /* @__PURE__ */ jsx(
653
- TableCell,
654
- {
655
- colSpan: emptyColSpan,
656
- className: "ui-data-table-empty",
657
- "aria-live": "polite",
658
- children: empty ?? /* @__PURE__ */ jsx(EmptyState, { title: t("dataTable.empty"), titleAs: "p" })
659
- }
660
- ) }) : dataRows.map((row) => {
661
- const original = row.original;
662
- const isSelected = row.getIsSelected();
663
- const isInteractiveTarget = (target) => !!target.closest(
664
- "button, a, input, select, textarea, [role=menuitem], [role=checkbox]"
665
- );
666
- return /* @__PURE__ */ jsxs(
667
- TableRow,
668
- {
669
- "data-state": isSelected ? "selected" : void 0,
670
- tabIndex: onRowClick ? 0 : void 0,
671
- onClick: (e) => {
672
- const target = e.target;
673
- if (isInteractiveTarget(target)) return;
674
- onRowClick?.(original);
675
- },
676
- onKeyDown: onRowClick ? (e) => {
677
- if (e.key !== "Enter" && e.key !== " ") return;
678
- if (e.target !== e.currentTarget) return;
679
- e.preventDefault();
680
- onRowClick?.(original);
681
- } : void 0,
682
- className: cn(
683
- rowPadding,
684
- // Hover highlight when rows are clickable OR explicitly hoverable…
685
- (onRowClick || hoverable) && "hover:bg-muted/50",
686
- // …but the affordance (cursor + focus mark) only when clickable.
687
- //
688
- // `ui-focus-ring` = the single focus source (styles/focus-ring.css). It
689
- // replaces `focus-visible:ring-ring focus-visible:ring-2
690
- // focus-visible:ring-inset`, which was a hand-rolled second ring: it read no
691
- // `--focus-ring-*` knob, so a service could not retune it, and — being a
692
- // utility — it painted regardless of the `--focus-outline` switch, which is
693
- // exactly the hole the switch exists to close.
694
- onRowClick && "ui-focus-ring cursor-pointer",
695
- isSelected && "bg-muted/30",
696
- rowClassName?.(original)
697
- ),
698
- children: [
699
- selectable && /* @__PURE__ */ jsx(TableCell, { className: cellPadding, children: /* @__PURE__ */ jsx(
700
- Checkbox,
1082
+ ) : error }) }) })
1083
+ ) : rowCount === 0 ? /* @__PURE__ */ jsx(TableRow, { className: "hover:bg-transparent", children: /* @__PURE__ */ jsx(
1084
+ TableCell,
1085
+ {
1086
+ colSpan: emptyColSpan,
1087
+ className: "ui-data-table-empty",
1088
+ "aria-live": "polite",
1089
+ children: empty ?? /* @__PURE__ */ jsx(EmptyState, { title: t("dataTable.empty"), titleAs: "p" })
1090
+ }
1091
+ ) }) : dataRows.map((row, rowIndex) => {
1092
+ const original = row.original;
1093
+ const rowKey = getRowId(original);
1094
+ const isSelected = row.getIsSelected();
1095
+ const checkboxProps = rowSelection?.getCheckboxProps?.(original) ?? {};
1096
+ const canExpand = expandColumnShown && (expandable?.rowExpandable?.(original) ?? true);
1097
+ const isExpanded = canExpand && expandedKeys.includes(rowKey);
1098
+ const rowProps = onRow?.(original, rowIndex) ?? {};
1099
+ const isInteractiveTarget = (target) => !!target.closest(
1100
+ "button, a, input, select, textarea, [role=menuitem], [role=checkbox], [role=radio]"
1101
+ );
1102
+ const expandByClick = canExpand && expandable?.expandRowByClick;
1103
+ return /* @__PURE__ */ jsxs(React.Fragment, { children: [
1104
+ /* @__PURE__ */ jsxs(
1105
+ TableRow,
701
1106
  {
702
- checked: isSelected,
703
- onCheckedChange: (v) => {
704
- row.toggleSelected(!!v);
705
- },
706
- "aria-label": t("dataTable.selectRow", { id: row.id }),
1107
+ ...rowProps,
1108
+ "data-state": isSelected ? "selected" : void 0,
1109
+ tabIndex: onRowClick ? 0 : rowProps.tabIndex,
707
1110
  onClick: (e) => {
708
- e.stopPropagation();
709
- }
710
- }
711
- ) }),
712
- visibleColumns.map((col) => /* @__PURE__ */ jsx(
713
- TableCell,
714
- {
715
- priority: col.priority,
716
- "data-align": col.align,
717
- style: columnWidth(col.width).style,
1111
+ rowProps.onClick?.(e);
1112
+ const target = e.target;
1113
+ if (isInteractiveTarget(target)) return;
1114
+ if (expandByClick) toggleExpanded(rowKey);
1115
+ onRowClick?.(original);
1116
+ },
1117
+ onKeyDown: onRowClick ? (e) => {
1118
+ rowProps.onKeyDown?.(e);
1119
+ if (e.key !== "Enter" && e.key !== " ") return;
1120
+ if (e.target !== e.currentTarget) return;
1121
+ e.preventDefault();
1122
+ onRowClick?.(original);
1123
+ } : rowProps.onKeyDown,
718
1124
  className: cn(
719
- cellPadding,
720
- columnWidth(col.width).className,
721
- col.align === "right" && "text-end",
722
- col.align === "center" && "text-center",
723
- col.hiddenOnMobile && "hidden md:table-cell",
724
- col.pin === "end" && "ui-data-table-pin-end"
1125
+ rowPadding,
1126
+ // Hover highlight when rows are clickable OR explicitly hoverable…
1127
+ (onRowClick || hoverable) && "hover:bg-muted/50",
1128
+ // …but the affordance (cursor + focus mark) only when clickable.
1129
+ //
1130
+ // `ui-focus-ring` = the single focus source (styles/focus-ring.css). It
1131
+ // replaces `focus-visible:ring-ring focus-visible:ring-2
1132
+ // focus-visible:ring-inset`, which was a hand-rolled second ring: it read no
1133
+ // `--focus-ring-*` knob, so a service could not retune it, and — being a
1134
+ // utility — it painted regardless of the `--focus-outline` switch, which is
1135
+ // exactly the hole the switch exists to close.
1136
+ onRowClick && "ui-focus-ring cursor-pointer",
1137
+ isSelected && "bg-muted/30",
1138
+ rowProps.className,
1139
+ rowClassName?.(original)
725
1140
  ),
726
- children: col.render ? col.render(original) : (() => {
727
- const v = original[col.key];
728
- const text = v == null || !(typeof v === "string" || typeof v === "number") ? "\u2014" : String(v);
729
- return /* @__PURE__ */ jsx("span", { "data-slot": "table-cell-text", children: text });
730
- })()
731
- },
732
- col.key
733
- ))
734
- ]
735
- },
736
- row.id
737
- );
738
- }) })
739
- ] })
1141
+ children: [
1142
+ expandColumnShown && /* @__PURE__ */ jsx(
1143
+ TableCell,
1144
+ {
1145
+ className: cn(
1146
+ cellPadding,
1147
+ leadingFixed === "start" && "ui-data-table-pin-start"
1148
+ ),
1149
+ ...fixedCellProps("__expand", leadingFixed),
1150
+ style: leadingFixedStyle("__expand"),
1151
+ children: canExpand ? /* @__PURE__ */ jsx(
1152
+ Button,
1153
+ {
1154
+ variant: "ghost",
1155
+ size: "icon-xs",
1156
+ "aria-expanded": isExpanded,
1157
+ "aria-label": isExpanded ? t("dataTable.collapseRow") : t("dataTable.expandRow"),
1158
+ className: "ui-data-table-expand-trigger",
1159
+ onClick: (e) => {
1160
+ e.stopPropagation();
1161
+ toggleExpanded(rowKey);
1162
+ },
1163
+ children: isExpanded ? /* @__PURE__ */ jsx(
1164
+ ChevronDown,
1165
+ {
1166
+ className: "ui-data-table-sort-icon",
1167
+ "aria-hidden": "true"
1168
+ }
1169
+ ) : /* @__PURE__ */ jsx(
1170
+ ChevronRight,
1171
+ {
1172
+ className: "ui-data-table-sort-icon",
1173
+ "aria-hidden": "true"
1174
+ }
1175
+ )
1176
+ }
1177
+ ) : null
1178
+ }
1179
+ ),
1180
+ selectable && /* @__PURE__ */ jsx(
1181
+ TableCell,
1182
+ {
1183
+ className: cn(
1184
+ cellPadding,
1185
+ leadingFixed === "start" && "ui-data-table-pin-start"
1186
+ ),
1187
+ ...fixedCellProps("__select", leadingFixed),
1188
+ style: leadingFixedStyle("__select"),
1189
+ children: rowSelection?.type === "radio" ? (
1190
+ // One choice per table, so each cell is its own single-item group:
1191
+ // Radix requires a Root for an Item, and a Root cannot legally span
1192
+ // <tr> boundaries without rewriting the table's own semantics.
1193
+ /* @__PURE__ */ jsx(
1194
+ RadioGroupRoot,
1195
+ {
1196
+ value: isSelected ? rowKey : "",
1197
+ disabled: checkboxProps.disabled,
1198
+ onValueChange: () => {
1199
+ row.toggleSelected(true);
1200
+ },
1201
+ children: /* @__PURE__ */ jsx(
1202
+ RadioItem,
1203
+ {
1204
+ value: rowKey,
1205
+ "aria-label": checkboxProps["aria-label"] ?? t("dataTable.selectRow", { id: row.id }),
1206
+ onClick: (e) => {
1207
+ e.stopPropagation();
1208
+ }
1209
+ }
1210
+ )
1211
+ }
1212
+ )
1213
+ ) : /* @__PURE__ */ jsx(
1214
+ Checkbox,
1215
+ {
1216
+ checked: isSelected,
1217
+ disabled: checkboxProps.disabled,
1218
+ onCheckedChange: (v) => {
1219
+ row.toggleSelected(!!v);
1220
+ },
1221
+ "aria-label": checkboxProps["aria-label"] ?? t("dataTable.selectRow", { id: row.id }),
1222
+ onClick: (e) => {
1223
+ e.stopPropagation();
1224
+ }
1225
+ }
1226
+ )
1227
+ }
1228
+ ),
1229
+ visibleColumns.map((col) => {
1230
+ const rendered = col.render ? col.render(original) : (() => {
1231
+ const v = original[col.key];
1232
+ const text = v == null || !(typeof v === "string" || typeof v === "number") ? "\u2014" : String(v);
1233
+ return /* @__PURE__ */ jsx("span", { "data-slot": "table-cell-text", children: text });
1234
+ })();
1235
+ const rawValue = original[col.key];
1236
+ const title = col.ellipsis && !col.render && (typeof rawValue === "string" || typeof rawValue === "number") ? String(rawValue) : void 0;
1237
+ return /* @__PURE__ */ jsx(
1238
+ TableCell,
1239
+ {
1240
+ priority: col.priority,
1241
+ "data-align": col.align,
1242
+ title,
1243
+ ...fixedCellProps(col.key, fixedEdge(col)),
1244
+ style: columnCellStyle(col),
1245
+ className: cn(cellPadding, columnCellClass(col)),
1246
+ children: rendered
1247
+ },
1248
+ col.key
1249
+ );
1250
+ })
1251
+ ]
1252
+ }
1253
+ ),
1254
+ isExpanded && expandable?.expandedRowRender ? /* @__PURE__ */ jsx(
1255
+ TableRow,
1256
+ {
1257
+ className: "ui-data-table-expanded-row hover:bg-transparent",
1258
+ "data-expanded-row": "",
1259
+ children: /* @__PURE__ */ jsx(TableCell, { colSpan: emptyColSpan, flush: true, children: expandable.expandedRowRender(original, rowIndex, true) })
1260
+ }
1261
+ ) : null
1262
+ ] }, row.id);
1263
+ }) }),
1264
+ summary ? (
1265
+ // A real <tfoot>: the totals row belongs to the table's own grid, so it keeps the
1266
+ // column widths, the header association and the screen-reader row navigation.
1267
+ /* @__PURE__ */ jsx("tfoot", { "data-slot": "table-footer", className: "ui-data-table-summary", children: summary(dataRows.map((row) => row.original)) })
1268
+ ) : null
1269
+ ]
1270
+ }
1271
+ )
740
1272
  }
741
1273
  )
742
1274
  }
@@ -779,14 +1311,14 @@ function CursorPagination({ cursor, hasMore, onChange, className }) {
779
1311
  )
780
1312
  ] });
781
1313
  }
782
- function NumberedPagination({
783
- pageSizeOptions = [10, 20, 50, 100],
784
- className
785
- }) {
786
- const { table } = useDataTableContext();
1314
+ function NumberedPagination({ pageSizeOptions, className }) {
1315
+ const { table, paginationConfig, pagerHidden } = useDataTableContext();
787
1316
  const { t } = useTranslation();
788
1317
  const { pageIndex, pageSize } = table.state.pagination;
789
1318
  const pageCount = table.getPageCount();
1319
+ if (pagerHidden) return null;
1320
+ const options = pageSizeOptions ?? paginationConfig?.pageSizeOptions ?? [10, 20, 50, 100];
1321
+ const showSizeChanger = paginationConfig?.showSizeChanger ?? true;
790
1322
  return /* @__PURE__ */ jsxs(
791
1323
  Flex,
792
1324
  {
@@ -797,7 +1329,7 @@ function NumberedPagination({
797
1329
  wrap: true,
798
1330
  className: cn("ui-data-table-pagination ui-data-table-pagination--numbered", className),
799
1331
  children: [
800
- /* @__PURE__ */ jsxs(Flex, { direction: "row", align: "center", gap: "sm", className: "ui-data-table-page-size", children: [
1332
+ showSizeChanger ? /* @__PURE__ */ jsxs(Flex, { direction: "row", align: "center", gap: "sm", className: "ui-data-table-page-size", children: [
801
1333
  /* @__PURE__ */ jsx("span", { className: "ui-data-table-page-size-label ui-data-table-pagination-text", children: t("dataGrid.rowsPerPage") }),
802
1334
  /* @__PURE__ */ jsxs(
803
1335
  Select,
@@ -814,11 +1346,11 @@ function NumberedPagination({
814
1346
  children: /* @__PURE__ */ jsx(SelectValue, {})
815
1347
  }
816
1348
  ),
817
- /* @__PURE__ */ jsx(SelectContent, { children: pageSizeOptions.map((n) => /* @__PURE__ */ jsx(SelectItem, { value: String(n), className: "tabular-nums", children: n }, n)) })
1349
+ /* @__PURE__ */ jsx(SelectContent, { children: options.map((n) => /* @__PURE__ */ jsx(SelectItem, { value: String(n), className: "tabular-nums", children: n }, n)) })
818
1350
  ]
819
1351
  }
820
1352
  )
821
- ] }),
1353
+ ] }) : /* @__PURE__ */ jsx("span", {}),
822
1354
  /* @__PURE__ */ jsxs(Flex, { direction: "row", align: "center", gap: "sm", className: "ui-data-table-page-nav", children: [
823
1355
  /* @__PURE__ */ jsx("span", { className: "ui-data-table-pagination-text tabular-nums", children: t("dataGrid.pageOf", { page: pageIndex + 1, total: Math.max(1, pageCount) }) }),
824
1356
  /* @__PURE__ */ jsx(