@eifi1/ui-kit 0.6.0 → 0.7.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 (271) hide show
  1. package/README.md +69 -33
  2. package/dist/chart.d.ts +1 -0
  3. package/dist/components/account-settings.js +3 -2
  4. package/dist/components/account-settings.js.map +1 -1
  5. package/dist/components/amount-input.d.ts +3 -0
  6. package/dist/components/amount-input.js +6 -4
  7. package/dist/components/amount-input.js.map +1 -1
  8. package/dist/components/autocomplete.d.ts +36 -7
  9. package/dist/components/autocomplete.js +68 -22
  10. package/dist/components/autocomplete.js.map +1 -1
  11. package/dist/components/calculator.d.ts +3 -0
  12. package/dist/components/calculator.js +2 -1
  13. package/dist/components/calculator.js.map +1 -1
  14. package/dist/components/chart-zoom.js +2 -1
  15. package/dist/components/chart-zoom.js.map +1 -1
  16. package/dist/components/chart.d.ts +33 -2
  17. package/dist/components/chart.js +20 -8
  18. package/dist/components/chart.js.map +1 -1
  19. package/dist/components/checkbox.d.ts +9 -0
  20. package/dist/components/checkbox.js +7 -2
  21. package/dist/components/checkbox.js.map +1 -1
  22. package/dist/components/chip.d.ts +34 -14
  23. package/dist/components/chip.js +63 -28
  24. package/dist/components/chip.js.map +1 -1
  25. package/dist/components/choice-card.d.ts +18 -1
  26. package/dist/components/choice-card.js +21 -8
  27. package/dist/components/choice-card.js.map +1 -1
  28. package/dist/components/combobox-core.d.ts +61 -2
  29. package/dist/components/combobox-core.js +68 -17
  30. package/dist/components/combobox-core.js.map +1 -1
  31. package/dist/components/combobox.d.ts +11 -7
  32. package/dist/components/combobox.js +51 -28
  33. package/dist/components/combobox.js.map +1 -1
  34. package/dist/components/currency-select.js +3 -3
  35. package/dist/components/currency-select.js.map +1 -1
  36. package/dist/components/danger-confirm.d.ts +30 -6
  37. package/dist/components/danger-confirm.js +51 -32
  38. package/dist/components/danger-confirm.js.map +1 -1
  39. package/dist/components/data-table-filter-popover.d.ts +1 -1
  40. package/dist/components/data-table-filters.d.ts +1 -1
  41. package/dist/components/data-table-pagination.d.ts +6 -3
  42. package/dist/components/data-table-pagination.js +6 -5
  43. package/dist/components/data-table-pagination.js.map +1 -1
  44. package/dist/components/data-table.d.ts +1 -1
  45. package/dist/components/data-table.js +332 -248
  46. package/dist/components/data-table.js.map +1 -1
  47. package/dist/components/date-picker.js +17 -7
  48. package/dist/components/date-picker.js.map +1 -1
  49. package/dist/components/dialog-frame.d.ts +28 -3
  50. package/dist/components/dialog-frame.js +47 -11
  51. package/dist/components/dialog-frame.js.map +1 -1
  52. package/dist/components/disclosure.d.ts +22 -2
  53. package/dist/components/disclosure.js +41 -8
  54. package/dist/components/disclosure.js.map +1 -1
  55. package/dist/components/dropdown.d.ts +7 -3
  56. package/dist/components/dropdown.js +6 -2
  57. package/dist/components/dropdown.js.map +1 -1
  58. package/dist/components/entity-combobox.d.ts +17 -8
  59. package/dist/components/entity-combobox.js +7 -5
  60. package/dist/components/entity-combobox.js.map +1 -1
  61. package/dist/components/file-button.d.ts +36 -5
  62. package/dist/components/file-button.js +42 -3
  63. package/dist/components/file-button.js.map +1 -1
  64. package/dist/components/file-dropzone.d.ts +9 -5
  65. package/dist/components/file-dropzone.js +14 -19
  66. package/dist/components/file-dropzone.js.map +1 -1
  67. package/dist/components/grouped-picker.js +2 -2
  68. package/dist/components/grouped-picker.js.map +1 -1
  69. package/dist/components/hover-menu.d.ts +8 -3
  70. package/dist/components/hover-menu.js +88 -4
  71. package/dist/components/hover-menu.js.map +1 -1
  72. package/dist/components/icon-picker.d.ts +10 -1
  73. package/dist/components/icon-picker.js +5 -1
  74. package/dist/components/icon-picker.js.map +1 -1
  75. package/dist/components/measured-grid.d.ts +165 -0
  76. package/dist/components/measured-grid.js +559 -0
  77. package/dist/components/measured-grid.js.map +1 -0
  78. package/dist/components/mini-calendar.js +7 -8
  79. package/dist/components/mini-calendar.js.map +1 -1
  80. package/dist/components/month-picker.js +13 -9
  81. package/dist/components/month-picker.js.map +1 -1
  82. package/dist/components/multi-entity-combobox.js +2 -2
  83. package/dist/components/multi-entity-combobox.js.map +1 -1
  84. package/dist/components/multi-select.d.ts +9 -1
  85. package/dist/components/multi-select.js +22 -8
  86. package/dist/components/multi-select.js.map +1 -1
  87. package/dist/components/number-field.d.ts +3 -0
  88. package/dist/components/number-input.d.ts +3 -0
  89. package/dist/components/number-input.js +3 -3
  90. package/dist/components/number-input.js.map +1 -1
  91. package/dist/components/numpad-sheet.d.ts +3 -0
  92. package/dist/components/numpad-sheet.js +3 -3
  93. package/dist/components/numpad-sheet.js.map +1 -1
  94. package/dist/components/picker-sheet.js +1 -1
  95. package/dist/components/picker-sheet.js.map +1 -1
  96. package/dist/components/popover.js +11 -2
  97. package/dist/components/popover.js.map +1 -1
  98. package/dist/components/series-chart-ticks.d.ts +13 -0
  99. package/dist/components/series-chart-ticks.js +29 -0
  100. package/dist/components/series-chart-ticks.js.map +1 -0
  101. package/dist/components/series-chart.js +33 -27
  102. package/dist/components/series-chart.js.map +1 -1
  103. package/dist/components/swatch-picker.d.ts +10 -1
  104. package/dist/components/swatch-picker.js +2 -0
  105. package/dist/components/swatch-picker.js.map +1 -1
  106. package/dist/components/swipeable-row.d.ts +11 -2
  107. package/dist/components/swipeable-row.js +5 -3
  108. package/dist/components/swipeable-row.js.map +1 -1
  109. package/dist/components/switch.d.ts +9 -0
  110. package/dist/components/switch.js +7 -2
  111. package/dist/components/switch.js.map +1 -1
  112. package/dist/components/tile-radio.js +5 -7
  113. package/dist/components/tile-radio.js.map +1 -1
  114. package/dist/components/toggle-group.d.ts +13 -3
  115. package/dist/components/toggle-group.js +21 -1
  116. package/dist/components/toggle-group.js.map +1 -1
  117. package/dist/components/toggle-legend.js +1 -2
  118. package/dist/components/toggle-legend.js.map +1 -1
  119. package/dist/components/treemap.d.ts +6 -1
  120. package/dist/components/treemap.js +16 -6
  121. package/dist/components/treemap.js.map +1 -1
  122. package/dist/components/ui.d.ts +31 -7
  123. package/dist/components/ui.js +26 -18
  124. package/dist/components/ui.js.map +1 -1
  125. package/dist/components/use-anchor-dir.d.ts +15 -0
  126. package/dist/components/use-anchor-dir.js +14 -0
  127. package/dist/components/use-anchor-dir.js.map +1 -0
  128. package/dist/components/use-mobile-reveal.d.ts +8 -1
  129. package/dist/components/use-mobile-reveal.js +3 -2
  130. package/dist/components/use-mobile-reveal.js.map +1 -1
  131. package/dist/components/use-table-state.d.ts +4 -4
  132. package/dist/components/use-table-state.js +12 -5
  133. package/dist/components/use-table-state.js.map +1 -1
  134. package/dist/{data-table-filters-DM1auUfH.d.ts → data-table-filters-noy0Abvi.d.ts} +40 -7
  135. package/dist/data-table.d.ts +1 -1
  136. package/dist/data-table.js.map +1 -1
  137. package/dist/feedback/feedback-attachment.d.ts +22 -3
  138. package/dist/feedback/feedback-attachment.js +15 -7
  139. package/dist/feedback/feedback-attachment.js.map +1 -1
  140. package/dist/feedback/feedback-dialog.d.ts +3 -2
  141. package/dist/feedback/feedback-dialog.js.map +1 -1
  142. package/dist/feedback.d.ts +1 -1
  143. package/dist/hooks/use-windowed-rows.d.ts +29 -0
  144. package/dist/hooks/use-windowed-rows.js +31 -0
  145. package/dist/hooks/use-windowed-rows.js.map +1 -0
  146. package/dist/i18n/defaults.d.ts +3 -0
  147. package/dist/i18n/defaults.js +5 -1
  148. package/dist/i18n/defaults.js.map +1 -1
  149. package/dist/i18n/kit-labels.d.ts +5 -0
  150. package/dist/i18n/kit-labels.js.map +1 -1
  151. package/dist/i18n/locales/de-CH.d.ts +43 -0
  152. package/dist/i18n/locales/de-CH.js +21 -0
  153. package/dist/i18n/locales/de-CH.js.map +1 -0
  154. package/dist/i18n/locales/de.d.ts +43 -0
  155. package/dist/i18n/locales/de.js +344 -0
  156. package/dist/i18n/locales/de.js.map +1 -0
  157. package/dist/i18n/locales/es.d.ts +43 -0
  158. package/dist/i18n/locales/es.js +339 -0
  159. package/dist/i18n/locales/es.js.map +1 -0
  160. package/dist/i18n/locales/fr.d.ts +43 -0
  161. package/dist/i18n/locales/fr.js +344 -0
  162. package/dist/i18n/locales/fr.js.map +1 -0
  163. package/dist/i18n/locales/hu.d.ts +43 -0
  164. package/dist/i18n/locales/hu.js +343 -0
  165. package/dist/i18n/locales/hu.js.map +1 -0
  166. package/dist/i18n/locales/it.d.ts +43 -0
  167. package/dist/i18n/locales/it.js +342 -0
  168. package/dist/i18n/locales/it.js.map +1 -0
  169. package/dist/i18n/locales/zh.d.ts +43 -0
  170. package/dist/i18n/locales/zh.js +338 -0
  171. package/dist/i18n/locales/zh.js.map +1 -0
  172. package/dist/index.d.ts +6 -3
  173. package/dist/index.js +4 -1
  174. package/dist/index.js.map +1 -1
  175. package/dist/lib/direction.d.ts +24 -0
  176. package/dist/lib/direction.js +19 -0
  177. package/dist/lib/direction.js.map +1 -0
  178. package/dist/rhf/form.js.map +1 -1
  179. package/dist/search/command-palette.d.ts +5 -0
  180. package/dist/search/command-palette.js +80 -50
  181. package/dist/search/command-palette.js.map +1 -1
  182. package/dist/shell/app-shell.d.ts +13 -1
  183. package/dist/shell/app-shell.js +133 -84
  184. package/dist/shell/app-shell.js.map +1 -1
  185. package/dist/shell/topbar-action-menu.js +1 -1
  186. package/dist/shell/topbar-action-menu.js.map +1 -1
  187. package/dist/shell/topbar-controls.d.ts +7 -3
  188. package/dist/shell/topbar-controls.js +38 -31
  189. package/dist/shell/topbar-controls.js.map +1 -1
  190. package/dist/theme/chart-palette.d.ts +7 -1
  191. package/dist/theme/chart-palette.js +2 -1
  192. package/dist/theme/chart-palette.js.map +1 -1
  193. package/dist/tour/tour.d.ts +11 -5
  194. package/dist/tour/tour.js +124 -58
  195. package/dist/tour/tour.js.map +1 -1
  196. package/dist/wizard/use-wizard.js +30 -13
  197. package/dist/wizard/use-wizard.js.map +1 -1
  198. package/dist/wizard/wizard-summary.js +1 -1
  199. package/dist/wizard/wizard-summary.js.map +1 -1
  200. package/package.json +12 -2
  201. package/src/components/account-settings.tsx +6 -2
  202. package/src/components/amount-input.tsx +10 -8
  203. package/src/components/autocomplete.tsx +123 -33
  204. package/src/components/calculator.tsx +5 -1
  205. package/src/components/chart-zoom.tsx +8 -1
  206. package/src/components/chart.tsx +68 -7
  207. package/src/components/checkbox.tsx +16 -0
  208. package/src/components/chip.tsx +131 -49
  209. package/src/components/choice-card.tsx +77 -36
  210. package/src/components/combobox-core.tsx +147 -20
  211. package/src/components/combobox.tsx +100 -44
  212. package/src/components/currency-select.tsx +6 -6
  213. package/src/components/danger-confirm.tsx +90 -36
  214. package/src/components/data-table-pagination.tsx +32 -19
  215. package/src/components/data-table.tsx +198 -36
  216. package/src/components/date-picker.tsx +24 -7
  217. package/src/components/dialog-frame.tsx +97 -22
  218. package/src/components/disclosure.tsx +75 -13
  219. package/src/components/dropdown.tsx +17 -5
  220. package/src/components/entity-combobox.tsx +26 -13
  221. package/src/components/file-button.tsx +102 -6
  222. package/src/components/file-dropzone.tsx +44 -35
  223. package/src/components/grouped-picker.tsx +2 -2
  224. package/src/components/hover-menu.tsx +139 -10
  225. package/src/components/icon-picker.tsx +14 -1
  226. package/src/components/measured-grid.tsx +852 -0
  227. package/src/components/mini-calendar.tsx +11 -8
  228. package/src/components/month-picker.tsx +18 -11
  229. package/src/components/multi-entity-combobox.tsx +2 -2
  230. package/src/components/multi-select.tsx +34 -7
  231. package/src/components/number-input.tsx +4 -4
  232. package/src/components/numpad-sheet.tsx +5 -3
  233. package/src/components/picker-sheet.tsx +1 -1
  234. package/src/components/popover.tsx +18 -3
  235. package/src/components/series-chart-ticks.ts +52 -0
  236. package/src/components/series-chart.tsx +39 -31
  237. package/src/components/swatch-picker.tsx +11 -0
  238. package/src/components/swipeable-row.tsx +21 -6
  239. package/src/components/switch.tsx +16 -0
  240. package/src/components/tile-radio.tsx +5 -7
  241. package/src/components/toggle-group.tsx +39 -3
  242. package/src/components/toggle-legend.tsx +5 -2
  243. package/src/components/treemap.tsx +28 -5
  244. package/src/components/ui.tsx +74 -30
  245. package/src/components/use-anchor-dir.ts +20 -0
  246. package/src/components/use-mobile-reveal.ts +10 -2
  247. package/src/components/use-table-state.ts +44 -7
  248. package/src/data-table.ts +1 -0
  249. package/src/feedback/feedback-attachment.tsx +36 -9
  250. package/src/feedback/feedback-dialog.tsx +3 -2
  251. package/src/hooks/use-windowed-rows.ts +69 -0
  252. package/src/i18n/defaults.ts +4 -0
  253. package/src/i18n/kit-labels.tsx +4 -0
  254. package/src/i18n/locales/de-CH.ts +30 -0
  255. package/src/i18n/locales/de.ts +365 -0
  256. package/src/i18n/locales/es.ts +358 -0
  257. package/src/i18n/locales/fr.ts +371 -0
  258. package/src/i18n/locales/hu.ts +359 -0
  259. package/src/i18n/locales/it.ts +360 -0
  260. package/src/i18n/locales/zh.ts +348 -0
  261. package/src/index.ts +10 -1
  262. package/src/lib/direction.ts +37 -0
  263. package/src/rhf/form.tsx +21 -0
  264. package/src/search/command-palette.tsx +77 -19
  265. package/src/shell/app-shell.tsx +200 -129
  266. package/src/shell/topbar-action-menu.tsx +1 -1
  267. package/src/shell/topbar-controls.tsx +19 -8
  268. package/src/theme/chart-palette.ts +9 -2
  269. package/src/tour/tour.tsx +119 -18
  270. package/src/wizard/use-wizard.ts +55 -17
  271. package/src/wizard/wizard-summary.tsx +1 -1
@@ -11,8 +11,7 @@ import {
11
11
  Filter,
12
12
  X
13
13
  } from "lucide-react";
14
- import { useSearchParams } from "react-router";
15
- import { Card } from "./ui.js";
14
+ import { Card, Spinner } from "./ui.js";
16
15
  import { cn } from "../lib/cn.js";
17
16
  import {
18
17
  defaultFilterState,
@@ -34,6 +33,25 @@ import { useAnnounce } from "../hooks/use-announce.js";
34
33
  import { resolveDataTableLabels } from "./data-table-labels.js";
35
34
  import { useKitLabelOverrides, useKitLocale } from "../i18n/kit-labels.js";
36
35
  import { Tooltip } from "./tooltip.js";
36
+ import { dirOf, isRtl } from "../lib/direction.js";
37
+ function physicalSwipe(actions, dir) {
38
+ const rtl = dir === "rtl";
39
+ return {
40
+ left: (rtl ? actions.end : actions.start) ?? actions.left,
41
+ right: (rtl ? actions.start : actions.end) ?? actions.right
42
+ };
43
+ }
44
+ const PHYSICAL_ALIGN = /(^|\s)((?:[\w-]+:)*)text-(left|right)(?=\s|$)/g;
45
+ function logicalAlign(className) {
46
+ return className?.replace(
47
+ PHYSICAL_ALIGN,
48
+ (token, lead, variants, side) => (
49
+ // Already scoped to one direction: that is a caller choosing the physical side.
50
+ /(^|:)(ltr|rtl):/.test(variants) ? token : `${lead}${variants}text-${side === "right" ? "end" : "start"}`
51
+ )
52
+ );
53
+ }
54
+ const END_ALIGN = /(^|\s)text-end(?=\s|$)/;
37
55
  function cleanAttrs(attrs) {
38
56
  if (!attrs) return void 0;
39
57
  const out = {};
@@ -108,6 +126,7 @@ function DataTable({
108
126
  leadingRow
109
127
  }) {
110
128
  const isServer = !!serverPagination;
129
+ const isLoading = !!serverPagination?.isLoading;
111
130
  const labelOverrides = useKitLabelOverrides("dataTable");
112
131
  const labels = useMemo(
113
132
  () => resolveDataTableLabels(
@@ -120,7 +139,6 @@ function DataTable({
120
139
  [labelOverrides, labelsProp]
121
140
  );
122
141
  const locale = useKitLocale(localeProp);
123
- const [, setSearchParams] = useSearchParams();
124
142
  const { announce, regionProps } = useAnnounce();
125
143
  const {
126
144
  sorts,
@@ -143,7 +161,6 @@ function DataTable({
143
161
  storageKey,
144
162
  storageKeyPrefix,
145
163
  urlSync,
146
- setSearchParams,
147
164
  defaultPageSize,
148
165
  sortsProp,
149
166
  filtersProp
@@ -315,6 +332,7 @@ function DataTable({
315
332
  pointerId: e.pointerId,
316
333
  startX: e.clientX,
317
334
  startWidth,
335
+ sign: isRtl(e.currentTarget) ? -1 : 1,
318
336
  hadWidth: widths[key],
319
337
  cells: columnCells(key),
320
338
  width: startWidth,
@@ -327,7 +345,7 @@ function DataTable({
327
345
  const moveResize = (e) => {
328
346
  const drag = resizing.current;
329
347
  if (!drag || drag.pointerId !== e.pointerId) return;
330
- drag.width = Math.max(40, Math.round(drag.startWidth + (e.clientX - drag.startX)));
348
+ drag.width = Math.max(40, Math.round(drag.startWidth + drag.sign * (e.clientX - drag.startX)));
331
349
  drag.moved = true;
332
350
  applyLiveWidth(drag.cells, drag.width);
333
351
  };
@@ -373,14 +391,22 @@ function DataTable({
373
391
  const mobilePrimaryCol = mobileColumns.find((c) => c.mobilePrimary) ?? mobileColumns[0] ?? null;
374
392
  const mobileSecondaryColumns = mobileColumns.filter((c) => c !== mobilePrimaryCol);
375
393
  const mobileCardLinkable = mobileColumns.every((c) => !c.noRowLink);
394
+ const [mobileDir, setMobileDir] = useState("ltr");
395
+ const mobileRootRef = (el) => {
396
+ if (!el) return;
397
+ const d = dirOf(el);
398
+ if (d !== mobileDir) setMobileDir(d);
399
+ };
376
400
  const { mobileSlice, canRevealMoreMobile, loadMoreRef } = useMobileReveal({
377
401
  rows: sorted,
378
402
  slice,
379
403
  isServer,
380
404
  isMdUp,
405
+ unpaged,
381
406
  defaultPageSize
382
407
  });
383
408
  const selectionAnchor = useRef(null);
409
+ const rangeClick = useRef(null);
384
410
  const canSelect = (row) => !!selection && (selection.isSelectable?.(row) ?? true);
385
411
  const selectRange = (toIndex) => {
386
412
  if (!selection || selectionAnchor.current === null) return false;
@@ -408,11 +434,12 @@ function DataTable({
408
434
  const tint = rowClassName?.(row);
409
435
  const interactive = !!onRowClick;
410
436
  const opensDetail = interactive && !!expandedRow;
411
- const swipe = mobileSwipeActions?.(row);
437
+ const swipeActions = mobileSwipeActions?.(row);
438
+ const swipe = swipeActions ? physicalSwipe(swipeActions, mobileDir) : null;
412
439
  const swipeEnabled = !!swipe && !expanded;
413
440
  const href = mobileCardLinkable ? rowHref?.(row) : void 0;
414
441
  const cardClass = cn(
415
- "w-full px-4 py-3 text-left flex items-center gap-3",
442
+ "w-full px-4 py-3 text-start flex items-center gap-3",
416
443
  // Live #320's other half: the card acknowledges the touch before the sheet
417
444
  // arrives. On a cold route the data can take a beat, and an unacknowledged tap
418
445
  // reads as "did that register?" — which is most of what "abrupt" means here.
@@ -432,7 +459,16 @@ function DataTable({
432
459
  ] }) }),
433
460
  opensDetail && // `aria-hidden`: the row already announces itself as a button with
434
461
  // `aria-expanded`, so the icon would only add a second, wordless stop.
435
- (mobileExpandAsDialog ? /* @__PURE__ */ jsx(ChevronRight, { "aria-hidden": true, className: "size-4 shrink-0 text-[var(--text-placeholder)]" }) : /* @__PURE__ */ jsx(
462
+ (mobileExpandAsDialog ? (
463
+ // Points INTO the sheet, which is the reading-forward direction.
464
+ /* @__PURE__ */ jsx(
465
+ ChevronRight,
466
+ {
467
+ "aria-hidden": true,
468
+ className: "size-4 shrink-0 text-[var(--text-placeholder)] rtl:-scale-x-100"
469
+ }
470
+ )
471
+ ) : /* @__PURE__ */ jsx(
436
472
  ChevronDown,
437
473
  {
438
474
  "aria-hidden": true,
@@ -493,7 +529,7 @@ function DataTable({
493
529
  }, []) : null;
494
530
  return /* @__PURE__ */ jsxs(Card, { flush: true, className: cn("overflow-clip", fillHeight && "md:flex md:flex-1 md:flex-col md:min-h-0"), children: [
495
531
  /* @__PURE__ */ jsx("span", { ...regionProps }),
496
- !isMdUp && /* @__PURE__ */ jsxs("div", { children: [
532
+ !isMdUp && /* @__PURE__ */ jsxs("div", { ref: mobileRootRef, children: [
497
533
  (!isServer || onFiltersChange) && /* @__PURE__ */ jsx(
498
534
  MobileFilters,
499
535
  {
@@ -509,21 +545,32 @@ function DataTable({
509
545
  locale
510
546
  }
511
547
  ),
512
- /* @__PURE__ */ jsxs("ul", { className: "divide-y divide-[var(--border)]", children: [
513
- mobileGroups ? mobileGroups.map((g) => /* @__PURE__ */ jsxs(Fragment2, { children: [
514
- /* @__PURE__ */ jsx("li", { className: "sticky top-0 z-10 bg-[var(--bg-surface-2)] px-4 py-1.5 text-xs font-semibold uppercase tracking-wide text-[var(--text-muted)] backdrop-blur", children: mobileGroupLabel ? mobileGroupLabel(g.key) : g.key }),
515
- g.rows.map(renderMobileRow)
516
- ] }, `hb-group:${g.key}`)) : mobileSlice.map(renderMobileRow),
517
- mobileSlice.length === 0 && /* @__PURE__ */ jsx("li", { className: "px-4 py-6 text-center text-sm text-[var(--text-muted)]", children: empty ?? "\u2014" }),
518
- canRevealMoreMobile && /* @__PURE__ */ jsx(
519
- "li",
520
- {
521
- ref: loadMoreRef,
522
- className: "px-4 py-4 text-center text-xs text-[var(--text-placeholder)]",
523
- children: labels.loading
524
- }
525
- )
526
- ] }),
548
+ /* @__PURE__ */ jsxs(
549
+ "ul",
550
+ {
551
+ className: cn(
552
+ "divide-y divide-[var(--border)]",
553
+ // Same busy treatment as the desktop body; see `isLoading` below.
554
+ isLoading && mobileSlice.length > 0 && "opacity-60 transition-opacity"
555
+ ),
556
+ "aria-busy": isLoading || void 0,
557
+ children: [
558
+ mobileGroups ? mobileGroups.map((g) => /* @__PURE__ */ jsxs(Fragment2, { children: [
559
+ /* @__PURE__ */ jsx("li", { className: "sticky top-0 z-10 bg-[var(--bg-surface-2)] px-4 py-1.5 text-xs font-semibold uppercase tracking-wide text-[var(--text-muted)] backdrop-blur", children: mobileGroupLabel ? mobileGroupLabel(g.key) : g.key }),
560
+ g.rows.map(renderMobileRow)
561
+ ] }, `hb-group:${g.key}`)) : mobileSlice.map(renderMobileRow),
562
+ mobileSlice.length === 0 && /* @__PURE__ */ jsx("li", { className: "px-4 py-6 text-center text-sm text-[var(--text-muted)]", children: isLoading ? /* @__PURE__ */ jsx(LoadingText, { label: labels.loading }) : empty ?? "\u2014" }),
563
+ canRevealMoreMobile && /* @__PURE__ */ jsx(
564
+ "li",
565
+ {
566
+ ref: loadMoreRef,
567
+ className: "px-4 py-4 text-center text-xs text-[var(--text-placeholder)]",
568
+ children: labels.loading
569
+ }
570
+ )
571
+ ]
572
+ }
573
+ ),
527
574
  isServer && /* @__PURE__ */ jsx(
528
575
  Pagination,
529
576
  {
@@ -532,7 +579,7 @@ function DataTable({
532
579
  pageSize: paginationPageSize,
533
580
  total: paginationTotal,
534
581
  onPage: goToPage,
535
- onPageSize: (n) => serverPagination.onPageSizeChange?.(n),
582
+ onPageSize: serverPagination.onPageSizeChange,
536
583
  labels,
537
584
  locale
538
585
  }
@@ -555,236 +602,262 @@ function DataTable({
555
602
  {
556
603
  className: cn("overflow-auto [scrollbar-gutter:stable]", fillHeight && "flex-1 min-h-0"),
557
604
  style: fillHeight ? void 0 : { maxHeight: maxBodyHeight },
558
- children: /* @__PURE__ */ jsxs("table", { className: "w-full text-sm", "aria-label": labels.table, children: [
559
- /* @__PURE__ */ jsx("thead", { className: "bg-[var(--bg-surface-2)] text-[var(--text-secondary)]", children: /* @__PURE__ */ jsxs("tr", { className: "text-left", children: [
560
- selection && /* @__PURE__ */ jsx(
561
- "th",
562
- {
563
- scope: "col",
564
- className: "sticky top-0 z-10 w-10 bg-[var(--bg-surface-2)] px-3 py-2 align-middle backdrop-blur-sm",
565
- children: /* @__PURE__ */ jsx(
566
- "input",
605
+ children: /* @__PURE__ */ jsxs(
606
+ "table",
607
+ {
608
+ className: "w-full text-sm",
609
+ "aria-label": labels.table,
610
+ "aria-busy": isLoading || void 0,
611
+ children: [
612
+ /* @__PURE__ */ jsx("thead", { className: "bg-[var(--bg-surface-2)] text-[var(--text-secondary)]", children: /* @__PURE__ */ jsxs("tr", { className: "text-start", children: [
613
+ selection && /* @__PURE__ */ jsx(
614
+ "th",
567
615
  {
568
- type: "checkbox",
569
- className: "size-4 align-middle accent-indigo-600",
570
- checked: selection.allSelected,
571
- ref: (el) => {
572
- if (el) el.indeterminate = selection.someSelected && !selection.allSelected;
573
- },
574
- onChange: (e) => selection.onToggleAll(e.target.checked),
575
- "aria-label": labels.selectAllRows
576
- }
577
- )
578
- }
579
- ),
580
- visibleColumns.map((col) => {
581
- const sortable = (!isServer || !!onSortsChange) && !!col.sortBy;
582
- const filter = !isServer || onFiltersChange ? resolveFilter(col) : null;
583
- const filterState = filters[col.key] ?? (filter ? defaultFilterState(filter) : void 0);
584
- const sortIdx = sorts.findIndex((s) => s.key === col.key);
585
- const active = sortIdx !== -1;
586
- const Icon = active ? sorts[sortIdx].dir === "asc" ? ArrowUp : ArrowDown : ArrowUpDown;
587
- const filterActive = filterState && isFilterActive(filterState);
588
- const isRightAligned = col.headClassName?.includes("text-right");
589
- const width = widths[col.key];
590
- return /* @__PURE__ */ jsxs(
591
- "th",
592
- {
593
- ref: (el) => {
594
- if (el) headRefs.set(col.key, el);
595
- else headRefs.delete(col.key);
596
- },
597
- scope: "col",
598
- "aria-sort": sortable ? sortIdx === 0 ? sorts[0].dir === "asc" ? "ascending" : "descending" : "none" : void 0,
599
- style: width ? { width, minWidth: width, maxWidth: width } : void 0,
600
- className: cn(
601
- "relative px-3 py-2 font-medium align-middle whitespace-nowrap",
602
- // Keep the header visible while the user scrolls the page.
603
- // Each <th> carries its own bg so the row doesn't render
604
- // transparent over the data rows underneath.
605
- "sticky top-0 z-10 bg-[var(--bg-surface-2)] backdrop-blur-sm",
606
- col.headClassName
607
- ),
608
- children: [
609
- /* @__PURE__ */ jsxs(
610
- "div",
616
+ scope: "col",
617
+ className: "sticky top-0 z-10 w-10 bg-[var(--bg-surface-2)] px-3 py-2 align-middle backdrop-blur-sm",
618
+ children: /* @__PURE__ */ jsx(
619
+ "input",
611
620
  {
612
- className: cn(
613
- "flex items-center gap-1",
614
- isRightAligned && "flex-row-reverse"
615
- ),
616
- children: [
617
- /* @__PURE__ */ jsx(Tooltip, { label: sortable ? labels.sortHint : void 0, portal: true, children: /* @__PURE__ */ jsxs(
618
- "button",
619
- {
620
- type: "button",
621
- onClick: (e) => sortable && toggleSort(col.key, e.shiftKey),
622
- disabled: !sortable,
623
- className: cn(
624
- "flex items-center gap-1 text-left",
625
- sortable && "hover:text-[var(--text-primary)]"
626
- ),
627
- children: [
628
- /* @__PURE__ */ jsx("span", { children: col.header }),
629
- sortable && /* @__PURE__ */ jsx(Icon, { className: cn("size-3", active ? "opacity-100" : "opacity-40") }),
630
- active && sorts.length > 1 && /* @__PURE__ */ jsx("span", { className: "text-[9px] font-semibold leading-none text-brand", children: sortIdx + 1 })
631
- ]
632
- }
633
- ) }),
634
- filter && /* @__PURE__ */ jsx(
635
- Popover,
636
- {
637
- width: filter.type === "date" ? 420 : void 0,
638
- labels: { panel: labels.filter },
639
- trigger: ({ toggle, ref }) => /* @__PURE__ */ jsx(
621
+ type: "checkbox",
622
+ className: "size-4 align-middle accent-indigo-600",
623
+ checked: selection.allSelected,
624
+ ref: (el) => {
625
+ if (el) el.indeterminate = selection.someSelected && !selection.allSelected;
626
+ },
627
+ onChange: (e) => selection.onToggleAll(e.target.checked),
628
+ "aria-label": labels.selectAllRows
629
+ }
630
+ )
631
+ }
632
+ ),
633
+ visibleColumns.map((col) => {
634
+ const sortable = (!isServer || !!onSortsChange) && !!col.sortBy;
635
+ const filter = !isServer || onFiltersChange ? resolveFilter(col) : null;
636
+ const filterState = filters[col.key] ?? (filter ? defaultFilterState(filter) : void 0);
637
+ const sortIdx = sorts.findIndex((s) => s.key === col.key);
638
+ const active = sortIdx !== -1;
639
+ const Icon = active ? sorts[sortIdx].dir === "asc" ? ArrowUp : ArrowDown : ArrowUpDown;
640
+ const filterActive = filterState && isFilterActive(filterState);
641
+ const headClass = logicalAlign(col.headClassName);
642
+ const isEndAligned = !!headClass && END_ALIGN.test(headClass);
643
+ const width = widths[col.key];
644
+ return /* @__PURE__ */ jsxs(
645
+ "th",
646
+ {
647
+ ref: (el) => {
648
+ if (el) headRefs.set(col.key, el);
649
+ else headRefs.delete(col.key);
650
+ },
651
+ scope: "col",
652
+ "aria-sort": sortable ? sortIdx === 0 ? sorts[0].dir === "asc" ? "ascending" : "descending" : "none" : void 0,
653
+ style: width ? { width, minWidth: width, maxWidth: width } : void 0,
654
+ className: cn(
655
+ "relative px-3 py-2 font-medium align-middle whitespace-nowrap",
656
+ // Keep the header visible while the user scrolls the page.
657
+ // Each <th> carries its own bg so the row doesn't render
658
+ // transparent over the data rows underneath.
659
+ "sticky top-0 z-10 bg-[var(--bg-surface-2)] backdrop-blur-sm",
660
+ headClass
661
+ ),
662
+ children: [
663
+ /* @__PURE__ */ jsxs(
664
+ "div",
665
+ {
666
+ className: cn(
667
+ "flex items-center gap-1",
668
+ isEndAligned && "flex-row-reverse"
669
+ ),
670
+ children: [
671
+ /* @__PURE__ */ jsx(Tooltip, { label: sortable ? labels.sortHint : void 0, portal: true, children: /* @__PURE__ */ jsxs(
640
672
  "button",
641
673
  {
642
674
  type: "button",
643
- ref,
644
- onClick: toggle,
645
- "aria-label": labels.filter,
675
+ onClick: (e) => sortable && toggleSort(col.key, e.shiftKey),
676
+ disabled: !sortable,
646
677
  className: cn(
647
- "rounded p-1 transition-colors",
648
- filterActive ? "bg-[var(--brand-bg)] text-[var(--brand-muted)]" : "text-[var(--text-placeholder)] hover:text-[var(--text-secondary)] hover:bg-[var(--bg-hover)]"
678
+ "flex items-center gap-1 text-start",
679
+ sortable && "hover:text-[var(--text-primary)]"
649
680
  ),
650
- children: /* @__PURE__ */ jsx(Filter, { className: "size-3" })
681
+ children: [
682
+ /* @__PURE__ */ jsx("span", { children: col.header }),
683
+ sortable && /* @__PURE__ */ jsx(Icon, { className: cn("size-3", active ? "opacity-100" : "opacity-40") }),
684
+ active && sorts.length > 1 && /* @__PURE__ */ jsx("span", { className: "text-[9px] font-semibold leading-none text-brand", children: sortIdx + 1 })
685
+ ]
651
686
  }
652
- ),
653
- children: () => /* @__PURE__ */ jsx(
654
- FilterPopover,
687
+ ) }),
688
+ filter && /* @__PURE__ */ jsx(
689
+ Popover,
655
690
  {
656
- column: col,
657
- state: filterState ?? defaultFilterState(filter),
658
- onChange: (next) => setFilterValue(col.key, next),
659
- onClear: () => clearFilter(col.key),
660
- selectOptions: selectOptionsByKey[col.key] ?? [],
661
- labels,
662
- locale
691
+ width: filter.type === "date" ? 420 : void 0,
692
+ labels: { panel: labels.filter },
693
+ trigger: ({ toggle, ref }) => /* @__PURE__ */ jsx(
694
+ "button",
695
+ {
696
+ type: "button",
697
+ ref,
698
+ onClick: toggle,
699
+ "aria-label": labels.filter,
700
+ className: cn(
701
+ "rounded p-1 transition-colors",
702
+ filterActive ? "bg-[var(--brand-bg)] text-[var(--brand-muted)]" : "text-[var(--text-placeholder)] hover:text-[var(--text-secondary)] hover:bg-[var(--bg-hover)]"
703
+ ),
704
+ children: /* @__PURE__ */ jsx(Filter, { className: "size-3" })
705
+ }
706
+ ),
707
+ children: () => /* @__PURE__ */ jsx(
708
+ FilterPopover,
709
+ {
710
+ column: col,
711
+ state: filterState ?? defaultFilterState(filter),
712
+ onChange: (next) => setFilterValue(col.key, next),
713
+ onClear: () => clearFilter(col.key),
714
+ selectOptions: selectOptionsByKey[col.key] ?? [],
715
+ labels,
716
+ locale
717
+ }
718
+ )
663
719
  }
664
720
  )
665
- }
666
- )
667
- ]
668
- }
669
- ),
670
- /* @__PURE__ */ jsx(
671
- "span",
721
+ ]
722
+ }
723
+ ),
724
+ /* @__PURE__ */ jsx(
725
+ "span",
726
+ {
727
+ role: "separator",
728
+ "aria-orientation": "vertical",
729
+ onPointerDown: (e) => startResize(e, col.key),
730
+ onPointerMove: moveResize,
731
+ onPointerUp: (e) => finishResize(e, true),
732
+ onPointerCancel: (e) => finishResize(e, false),
733
+ onDoubleClick: (e) => {
734
+ e.preventDefault();
735
+ setWidths((w) => {
736
+ const next = { ...w };
737
+ delete next[col.key];
738
+ return next;
739
+ });
740
+ },
741
+ className: "absolute end-0 top-0 z-10 h-full w-1.5 -translate-x-1/2 rtl:translate-x-1/2 cursor-col-resize select-none touch-none hover:bg-[var(--brand-bg-hover)]"
742
+ }
743
+ )
744
+ ]
745
+ },
746
+ col.key
747
+ );
748
+ })
749
+ ] }) }),
750
+ /* @__PURE__ */ jsxs(
751
+ "tbody",
752
+ {
753
+ className: cn(isLoading && slice.length > 0 && "opacity-60 transition-opacity"),
754
+ children: [
755
+ leadingRow && /* @__PURE__ */ jsx("tr", { className: "border-t border-[var(--border)]", children: /* @__PURE__ */ jsx("td", { colSpan: totalColSpan, className: "p-0", children: leadingRow }) }),
756
+ slice.map((row, rowIndex) => {
757
+ const expanded = isExpanded?.(row) ?? false;
758
+ const expansion = expanded ? expandedRow?.(row) : null;
759
+ const rowInteractive = !!onRowClick || !!selection;
760
+ const href = rowHref?.(row);
761
+ return /* @__PURE__ */ jsxs(Fragment2, { children: [
762
+ /* @__PURE__ */ jsxs(
763
+ "tr",
764
+ {
765
+ ...cleanAttrs(rowAttributes?.(row)),
766
+ className: cn(
767
+ "border-t border-[var(--border)]",
768
+ rowInteractive && "cursor-pointer hover:bg-[var(--bg-hover)]",
769
+ rowClassName?.(row)
770
+ ),
771
+ onClick: rowInteractive ? (e) => {
772
+ if (selection && canSelect(row)) {
773
+ if (e.shiftKey) {
774
+ window.getSelection?.()?.removeAllRanges();
775
+ if (selectRange(rowIndex)) return;
776
+ selectionAnchor.current = rowKey(row);
777
+ selection.onToggle(row, !selection.isSelected(row));
778
+ return;
779
+ }
780
+ if (e.metaKey || e.ctrlKey) {
781
+ selectionAnchor.current = rowKey(row);
782
+ selection.onToggle(row, !selection.isSelected(row));
783
+ return;
784
+ }
785
+ }
786
+ onRowClick?.(row);
787
+ } : void 0,
788
+ children: [
789
+ selection && /* @__PURE__ */ jsx(
790
+ "td",
791
+ {
792
+ className: "w-10 px-3 py-2 align-top",
793
+ onClick: (e) => e.stopPropagation(),
794
+ children: canSelect(row) && /* @__PURE__ */ jsx(
795
+ "input",
796
+ {
797
+ type: "checkbox",
798
+ className: "size-4 accent-indigo-600",
799
+ checked: selection.isSelected(row),
800
+ onClick: (e) => {
801
+ if (e.shiftKey && selectRange(rowIndex)) {
802
+ e.preventDefault();
803
+ rangeClick.current = e.nativeEvent;
804
+ }
805
+ },
806
+ onChange: (e) => {
807
+ if (rangeClick.current && e.nativeEvent === rangeClick.current) {
808
+ rangeClick.current = null;
809
+ return;
810
+ }
811
+ selectionAnchor.current = rowKey(row);
812
+ selection.onToggle(row, e.target.checked);
813
+ },
814
+ "aria-label": labels.selectRow
815
+ }
816
+ )
817
+ }
818
+ ),
819
+ visibleColumns.map((col) => {
820
+ const width = widths[col.key];
821
+ return /* @__PURE__ */ jsx(
822
+ "td",
823
+ {
824
+ "data-col": col.key,
825
+ style: width ? { width, minWidth: width, maxWidth: width } : void 0,
826
+ className: cn(
827
+ "px-3 py-2 align-top",
828
+ width && "overflow-hidden text-ellipsis",
829
+ logicalAlign(col.className)
830
+ ),
831
+ children: col === linkColumn && href ? (
832
+ // No `onActivate`: the click is left to bubble to the
833
+ // <tr> handler above, which already owns expand-vs-select.
834
+ // Calling it here as well would toggle the row twice.
835
+ /* @__PURE__ */ jsx(RowLink, { href, className: "block", children: col.cell(row) })
836
+ ) : col.cell(row)
837
+ },
838
+ col.key
839
+ );
840
+ })
841
+ ]
842
+ }
843
+ ),
844
+ expansion && /* @__PURE__ */ jsx("tr", { className: "bg-[var(--bg-surface-2)] border-t border-[var(--border)]", children: /* @__PURE__ */ jsx("td", { colSpan: totalColSpan, className: "p-0", children: /* @__PURE__ */ jsx("div", { className: "w-0 min-w-full px-3 py-3", children: expansion }) }) })
845
+ ] }, rowKey(row));
846
+ }),
847
+ slice.length === 0 && /* @__PURE__ */ jsx("tr", { children: /* @__PURE__ */ jsx(
848
+ "td",
672
849
  {
673
- role: "separator",
674
- "aria-orientation": "vertical",
675
- onPointerDown: (e) => startResize(e, col.key),
676
- onPointerMove: moveResize,
677
- onPointerUp: (e) => finishResize(e, true),
678
- onPointerCancel: (e) => finishResize(e, false),
679
- onDoubleClick: (e) => {
680
- e.preventDefault();
681
- setWidths((w) => {
682
- const next = { ...w };
683
- delete next[col.key];
684
- return next;
685
- });
686
- },
687
- className: "absolute right-0 top-0 z-10 h-full w-1.5 -translate-x-1/2 cursor-col-resize select-none touch-none hover:bg-[var(--brand-bg-hover)]"
850
+ colSpan: totalColSpan,
851
+ className: "px-3 py-4 text-center text-[var(--text-muted)]",
852
+ children: isLoading ? /* @__PURE__ */ jsx(LoadingText, { label: labels.loading }) : empty ?? "\u2014"
688
853
  }
689
- )
854
+ ) })
690
855
  ]
691
- },
692
- col.key
693
- );
694
- })
695
- ] }) }),
696
- /* @__PURE__ */ jsxs("tbody", { children: [
697
- leadingRow && /* @__PURE__ */ jsx("tr", { className: "border-t border-[var(--border)]", children: /* @__PURE__ */ jsx("td", { colSpan: totalColSpan, className: "p-0", children: leadingRow }) }),
698
- slice.map((row, rowIndex) => {
699
- const expanded = isExpanded?.(row) ?? false;
700
- const expansion = expanded ? expandedRow?.(row) : null;
701
- const rowInteractive = !!onRowClick || !!selection;
702
- const href = rowHref?.(row);
703
- return /* @__PURE__ */ jsxs(Fragment2, { children: [
704
- /* @__PURE__ */ jsxs(
705
- "tr",
706
- {
707
- ...cleanAttrs(rowAttributes?.(row)),
708
- className: cn(
709
- "border-t border-[var(--border)]",
710
- rowInteractive && "cursor-pointer hover:bg-[var(--bg-hover)]",
711
- rowClassName?.(row)
712
- ),
713
- onClick: rowInteractive ? (e) => {
714
- if (selection && canSelect(row)) {
715
- if (e.shiftKey) {
716
- window.getSelection?.()?.removeAllRanges();
717
- if (selectRange(rowIndex)) return;
718
- selectionAnchor.current = rowKey(row);
719
- selection.onToggle(row, !selection.isSelected(row));
720
- return;
721
- }
722
- if (e.metaKey || e.ctrlKey) {
723
- selectionAnchor.current = rowKey(row);
724
- selection.onToggle(row, !selection.isSelected(row));
725
- return;
726
- }
727
- }
728
- onRowClick?.(row);
729
- } : void 0,
730
- children: [
731
- selection && /* @__PURE__ */ jsx(
732
- "td",
733
- {
734
- className: "w-10 px-3 py-2 align-top",
735
- onClick: (e) => e.stopPropagation(),
736
- children: canSelect(row) && /* @__PURE__ */ jsx(
737
- "input",
738
- {
739
- type: "checkbox",
740
- className: "size-4 accent-indigo-600",
741
- checked: selection.isSelected(row),
742
- onClick: (e) => {
743
- if (e.shiftKey && selectRange(rowIndex)) e.preventDefault();
744
- },
745
- onChange: (e) => {
746
- selectionAnchor.current = rowKey(row);
747
- selection.onToggle(row, e.target.checked);
748
- },
749
- "aria-label": labels.selectRow
750
- }
751
- )
752
- }
753
- ),
754
- visibleColumns.map((col) => {
755
- const width = widths[col.key];
756
- return /* @__PURE__ */ jsx(
757
- "td",
758
- {
759
- "data-col": col.key,
760
- style: width ? { width, minWidth: width, maxWidth: width } : void 0,
761
- className: cn("px-3 py-2 align-top", width && "overflow-hidden text-ellipsis", col.className),
762
- children: col === linkColumn && href ? (
763
- // No `onActivate`: the click is left to bubble to the
764
- // <tr> handler above, which already owns expand-vs-select.
765
- // Calling it here as well would toggle the row twice.
766
- /* @__PURE__ */ jsx(RowLink, { href, className: "block", children: col.cell(row) })
767
- ) : col.cell(row)
768
- },
769
- col.key
770
- );
771
- })
772
- ]
773
- }
774
- ),
775
- expansion && /* @__PURE__ */ jsx("tr", { className: "bg-[var(--bg-surface-2)] border-t border-[var(--border)]", children: /* @__PURE__ */ jsx("td", { colSpan: totalColSpan, className: "p-0", children: /* @__PURE__ */ jsx("div", { className: "w-0 min-w-full px-3 py-3", children: expansion }) }) })
776
- ] }, rowKey(row));
777
- }),
778
- slice.length === 0 && /* @__PURE__ */ jsx("tr", { children: /* @__PURE__ */ jsx(
779
- "td",
780
- {
781
- colSpan: totalColSpan,
782
- className: "px-3 py-4 text-center text-[var(--text-muted)]",
783
- children: empty ?? "\u2014"
784
- }
785
- ) })
786
- ] })
787
- ] })
856
+ }
857
+ )
858
+ ]
859
+ }
860
+ )
788
861
  }
789
862
  ),
790
863
  !unpaged && /* @__PURE__ */ jsx(
@@ -795,7 +868,7 @@ function DataTable({
795
868
  pageSize: paginationPageSize,
796
869
  total: paginationTotal,
797
870
  onPage: goToPage,
798
- onPageSize: isServer ? (n) => serverPagination.onPageSizeChange?.(n) : (n) => {
871
+ onPageSize: isServer ? serverPagination.onPageSizeChange : (n) => {
799
872
  setPageSize(n);
800
873
  setPage(0);
801
874
  },
@@ -814,9 +887,9 @@ function DataTable({
814
887
  tabIndex: showSettings ? -1 : 0,
815
888
  className: cn(
816
889
  "group flex shrink-0 items-start justify-center overflow-hidden pt-3 transition-[width] duration-200 ease-out",
817
- "border-l border-[var(--border)]",
890
+ "border-s border-[var(--border)]",
818
891
  "hover:bg-[var(--bg-hover)]",
819
- showSettings ? "w-0 border-l-0" : "w-8 cursor-pointer"
892
+ showSettings ? "w-0 border-s-0" : "w-8 cursor-pointer"
820
893
  ),
821
894
  children: /* @__PURE__ */ jsx(
822
895
  "span",
@@ -835,7 +908,7 @@ function DataTable({
835
908
  "shrink-0 overflow-hidden transition-[width] duration-200 ease-out",
836
909
  showSettings ? "w-56" : "w-0"
837
910
  ),
838
- children: /* @__PURE__ */ jsxs("div", { className: "flex w-56 flex-col border-l border-[var(--border)] p-2", children: [
911
+ children: /* @__PURE__ */ jsxs("div", { className: "flex w-56 flex-col border-s border-[var(--border)] p-2", children: [
839
912
  /* @__PURE__ */ jsxs("div", { className: "mb-2 flex items-center justify-between gap-1", children: [
840
913
  /* @__PURE__ */ jsx("div", { className: "text-xs font-medium uppercase tracking-wide text-[var(--text-muted)]", children: columnsCountLabel }),
841
914
  /* @__PURE__ */ jsx(Tooltip, { label: labels.close, portal: true, children: /* @__PURE__ */ jsx(
@@ -894,6 +967,12 @@ function DataTable({
894
967
  ] })
895
968
  ] });
896
969
  }
970
+ function LoadingText({ label }) {
971
+ return /* @__PURE__ */ jsxs("span", { className: "inline-flex items-center gap-2", children: [
972
+ /* @__PURE__ */ jsx(Spinner, { label: null, className: "size-4" }),
973
+ label
974
+ ] });
975
+ }
897
976
  function MobileFilters({
898
977
  columns,
899
978
  filters,
@@ -907,6 +986,7 @@ function MobileFilters({
907
986
  locale
908
987
  }) {
909
988
  const [open, setOpen] = useState(false);
989
+ const [dir, setDir] = useState("ltr");
910
990
  const [expanded, setExpanded] = useState(null);
911
991
  const backdropClose = useBackdropClose(() => setOpen(false));
912
992
  useBodyScrollLock(open);
@@ -925,7 +1005,10 @@ function MobileFilters({
925
1005
  "button",
926
1006
  {
927
1007
  type: "button",
928
- onClick: () => setOpen(true),
1008
+ onClick: (e) => {
1009
+ setDir(dirOf(e.currentTarget));
1010
+ setOpen(true);
1011
+ },
929
1012
  className: "inline-flex items-center gap-1.5 rounded-md px-2 py-1 text-sm font-medium text-[var(--text-secondary)] hover:bg-[var(--bg-hover)]",
930
1013
  children: [
931
1014
  /* @__PURE__ */ jsx(Filter, { className: "size-4" }),
@@ -950,6 +1033,7 @@ function MobileFilters({
950
1033
  className: "fixed inset-0 z-50 flex items-end justify-center bg-black/40",
951
1034
  role: "dialog",
952
1035
  "aria-modal": "true",
1036
+ dir,
953
1037
  ...backdropClose,
954
1038
  children: /* @__PURE__ */ jsxs("div", { className: "flex max-h-[85vh] w-full flex-col overflow-hidden rounded-t-2xl bg-[var(--bg-surface)] shadow-xl", children: [
955
1039
  /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-2 border-b border-[var(--border)] px-4 py-3", children: [
@@ -960,7 +1044,7 @@ function MobileFilters({
960
1044
  type: "button",
961
1045
  onClick: () => setOpen(false),
962
1046
  "aria-label": labels.close,
963
- className: "-mr-1 rounded p-1.5 text-[var(--text-muted)] hover:bg-[var(--bg-hover)] hover:text-[var(--text-secondary)]",
1047
+ className: "-me-1 rounded p-1.5 text-[var(--text-muted)] hover:bg-[var(--bg-hover)] hover:text-[var(--text-secondary)]",
964
1048
  children: /* @__PURE__ */ jsx(X, { className: "size-5" })
965
1049
  }
966
1050
  )
@@ -976,7 +1060,7 @@ function MobileFilters({
976
1060
  type: "button",
977
1061
  onClick: () => setExpanded(isItemOpen ? null : col.key),
978
1062
  "aria-expanded": isItemOpen,
979
- className: "flex w-full items-center justify-between gap-2 px-4 py-3 text-left",
1063
+ className: "flex w-full items-center justify-between gap-2 px-4 py-3 text-start",
980
1064
  children: [
981
1065
  /* @__PURE__ */ jsxs("span", { className: "flex items-center gap-2 text-sm font-medium text-[var(--text-secondary)]", children: [
982
1066
  col.header,