@eifi1/ui-kit 0.34.0 → 0.35.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 (241) hide show
  1. package/README.md +82 -38
  2. package/dist/billing/billing-banner.d.ts +5 -2
  3. package/dist/billing/billing-banner.js.map +1 -1
  4. package/dist/billing/plan-limit-notice.d.ts +5 -2
  5. package/dist/billing/plan-limit-notice.js.map +1 -1
  6. package/dist/chart.js.map +1 -1
  7. package/dist/components/amount-input.d.ts +0 -3
  8. package/dist/components/amount-input.js +2 -2
  9. package/dist/components/amount-input.js.map +1 -1
  10. package/dist/components/calculator.d.ts +1 -4
  11. package/dist/components/calculator.js +1 -3
  12. package/dist/components/calculator.js.map +1 -1
  13. package/dist/components/chart-zoom.d.ts +0 -37
  14. package/dist/components/chart-zoom.js +1 -28
  15. package/dist/components/chart-zoom.js.map +1 -1
  16. package/dist/components/chip-input.d.ts +0 -4
  17. package/dist/components/chip-input.js +1 -5
  18. package/dist/components/chip-input.js.map +1 -1
  19. package/dist/components/combobox.d.ts +1 -1
  20. package/dist/components/combobox.js.map +1 -1
  21. package/dist/components/copy-button.d.ts +5 -1
  22. package/dist/components/copy-button.js.map +1 -1
  23. package/dist/components/currency-option-list.d.ts +3 -8
  24. package/dist/components/currency-option-list.js +1 -29
  25. package/dist/components/currency-option-list.js.map +1 -1
  26. package/dist/components/danger-confirm.d.ts +3 -3
  27. package/dist/components/danger-confirm.js.map +1 -1
  28. package/dist/components/data-table-filter-popover.d.ts +1 -1
  29. package/dist/components/data-table-filter-popover.js +1 -1
  30. package/dist/components/data-table-filter-popover.js.map +1 -1
  31. package/dist/components/data-table-labels.d.ts +1 -42
  32. package/dist/components/data-table-labels.js +2 -31
  33. package/dist/components/data-table-labels.js.map +1 -1
  34. package/dist/components/data-table-shared.d.ts +17 -1
  35. package/dist/components/data-table-shared.js +22 -0
  36. package/dist/components/data-table-shared.js.map +1 -1
  37. package/dist/components/data-table.d.ts +12 -2
  38. package/dist/components/data-table.js +18 -14
  39. package/dist/components/data-table.js.map +1 -1
  40. package/dist/components/dialog-frame.d.ts +3 -1
  41. package/dist/components/dialog-frame.js.map +1 -1
  42. package/dist/components/dropdown.d.ts +18 -15
  43. package/dist/components/dropdown.js +11 -3
  44. package/dist/components/dropdown.js.map +1 -1
  45. package/dist/components/entity-combobox.d.ts +1 -1
  46. package/dist/components/entity-combobox.js.map +1 -1
  47. package/dist/components/field-base.d.ts +0 -5
  48. package/dist/components/field-base.js +0 -5
  49. package/dist/components/field-base.js.map +1 -1
  50. package/dist/components/field-parts.d.ts +3 -11
  51. package/dist/components/field-parts.js +1 -9
  52. package/dist/components/field-parts.js.map +1 -1
  53. package/dist/components/field-strip.d.ts +8 -23
  54. package/dist/components/field-strip.js +24 -22
  55. package/dist/components/field-strip.js.map +1 -1
  56. package/dist/components/field-sync.d.ts +0 -4
  57. package/dist/components/field-sync.js +0 -4
  58. package/dist/components/field-sync.js.map +1 -1
  59. package/dist/components/field.d.ts +4 -4
  60. package/dist/components/field.js.map +1 -1
  61. package/dist/components/full-bleed-dialog.js +2 -2
  62. package/dist/components/full-bleed-dialog.js.map +1 -1
  63. package/dist/components/hover-menu.d.ts +6 -13
  64. package/dist/components/hover-menu.js +3 -3
  65. package/dist/components/hover-menu.js.map +1 -1
  66. package/dist/components/inline-entity-combobox.d.ts +1 -1
  67. package/dist/components/inline-entity-combobox.js.map +1 -1
  68. package/dist/components/modal.d.ts +9 -1
  69. package/dist/components/modal.js +7 -4
  70. package/dist/components/modal.js.map +1 -1
  71. package/dist/components/multi-select.d.ts +5 -16
  72. package/dist/components/multi-select.js +5 -10
  73. package/dist/components/multi-select.js.map +1 -1
  74. package/dist/components/number-input.d.ts +0 -3
  75. package/dist/components/number-input.js +2 -4
  76. package/dist/components/number-input.js.map +1 -1
  77. package/dist/components/popover.js +2 -2
  78. package/dist/components/popover.js.map +1 -1
  79. package/dist/components/series-chart.js.map +1 -1
  80. package/dist/components/server-wake.d.ts +0 -6
  81. package/dist/components/server-wake.js.map +1 -1
  82. package/dist/components/settings-fields.d.ts +4 -2
  83. package/dist/components/settings-fields.js.map +1 -1
  84. package/dist/components/stat-tile.d.ts +8 -2
  85. package/dist/components/stat-tile.js.map +1 -1
  86. package/dist/components/swipeable-row.d.ts +2 -17
  87. package/dist/components/swipeable-row.js +11 -16
  88. package/dist/components/swipeable-row.js.map +1 -1
  89. package/dist/components/tooltip.d.ts +9 -1
  90. package/dist/components/tooltip.js +20 -5
  91. package/dist/components/tooltip.js.map +1 -1
  92. package/dist/components/translation-review.d.ts +10 -2
  93. package/dist/components/translation-review.js +11 -3
  94. package/dist/components/translation-review.js.map +1 -1
  95. package/dist/components/use-table-state.d.ts +31 -3
  96. package/dist/components/use-table-state.js +53 -8
  97. package/dist/components/use-table-state.js.map +1 -1
  98. package/dist/components/user-avatar.d.ts +2 -2
  99. package/dist/components/user-avatar.js.map +1 -1
  100. package/dist/components/wizard-stepper.d.ts +1 -4
  101. package/dist/components/wizard-stepper.js +2 -3
  102. package/dist/components/wizard-stepper.js.map +1 -1
  103. package/dist/currencies.d.ts +23 -0
  104. package/dist/currencies.js +38 -0
  105. package/dist/currencies.js.map +1 -0
  106. package/dist/demo/demo-banner.d.ts +7 -2
  107. package/dist/demo/demo-banner.js.map +1 -1
  108. package/dist/eslint/index.d.ts +83 -0
  109. package/dist/eslint/index.js +69 -0
  110. package/dist/eslint/index.js.map +1 -0
  111. package/dist/eslint/role-utilities.d.ts +48 -0
  112. package/dist/eslint/role-utilities.generated.d.ts +2 -0
  113. package/dist/eslint/role-utilities.generated.js +143 -0
  114. package/dist/eslint/role-utilities.generated.js.map +1 -0
  115. package/dist/eslint/role-utilities.js +43 -0
  116. package/dist/eslint/role-utilities.js.map +1 -0
  117. package/dist/feedback/feedback-menu.d.ts +2 -2
  118. package/dist/feedback/feedback-menu.js.map +1 -1
  119. package/dist/feedback/feedback-thread.d.ts +3 -8
  120. package/dist/feedback/feedback-thread.js +0 -2
  121. package/dist/feedback/feedback-thread.js.map +1 -1
  122. package/dist/hooks/use-dismiss.d.ts +16 -3
  123. package/dist/hooks/use-dismiss.js +14 -2
  124. package/dist/hooks/use-dismiss.js.map +1 -1
  125. package/dist/i18n/kit-labels.d.ts +14 -4
  126. package/dist/i18n/kit-labels.js.map +1 -1
  127. package/dist/i18n/locales/en.js +1 -1
  128. package/dist/i18n/locales/en.js.map +2 -2
  129. package/dist/i18n/locales/fr.js +3 -2
  130. package/dist/i18n/locales/fr.js.map +1 -1
  131. package/dist/i18n/locales/it.js +3 -2
  132. package/dist/i18n/locales/it.js.map +1 -1
  133. package/dist/index.d.ts +2 -1
  134. package/dist/index.js +2 -2
  135. package/dist/index.js.map +1 -1
  136. package/dist/lib/refs.d.ts +13 -0
  137. package/dist/lib/refs.js +15 -0
  138. package/dist/lib/refs.js.map +1 -0
  139. package/dist/rhf/fields.d.ts +10 -10
  140. package/dist/rhf/fields.js.map +1 -1
  141. package/dist/rhf/form-core.d.ts +5 -4
  142. package/dist/rhf/form-core.js.map +1 -1
  143. package/dist/rhf/form.d.ts +7 -36
  144. package/dist/rhf/form.js +1 -16
  145. package/dist/rhf/form.js.map +1 -1
  146. package/dist/rhf/line-items.d.ts +1 -1
  147. package/dist/rhf/line-items.js.map +1 -1
  148. package/dist/rhf/use-rhf-wizard-step.js.map +1 -1
  149. package/dist/rhf.js.map +1 -1
  150. package/dist/shell/option-switcher-menu.d.ts +5 -17
  151. package/dist/shell/option-switcher-menu.js +1 -3
  152. package/dist/shell/option-switcher-menu.js.map +1 -1
  153. package/dist/shell/role-switcher.d.ts +1 -4
  154. package/dist/shell/role-switcher.js +2 -3
  155. package/dist/shell/role-switcher.js.map +1 -1
  156. package/dist/shell/topbar-action-menu.d.ts +7 -19
  157. package/dist/shell/topbar-action-menu.js +1 -3
  158. package/dist/shell/topbar-action-menu.js.map +1 -1
  159. package/dist/shell/topbar-controls.d.ts +3 -12
  160. package/dist/shell/topbar-controls.js +6 -9
  161. package/dist/shell/topbar-controls.js.map +1 -1
  162. package/dist/testing/index.d.ts +1 -1
  163. package/dist/testing/index.js +2 -0
  164. package/dist/testing/index.js.map +1 -1
  165. package/dist/testing/viewport.d.ts +25 -2
  166. package/dist/testing/viewport.js +61 -10
  167. package/dist/testing/viewport.js.map +1 -1
  168. package/dist/theme/palette-presets.d.ts +0 -7
  169. package/dist/theme/palette-presets.js +0 -279
  170. package/dist/theme/palette-presets.js.map +1 -1
  171. package/dist/wizard/types.d.ts +0 -5
  172. package/dist/wizard/types.js +1 -5
  173. package/dist/wizard/types.js.map +1 -1
  174. package/package.json +9 -1
  175. package/src/billing/billing-banner.tsx +5 -2
  176. package/src/billing/plan-limit-notice.tsx +5 -2
  177. package/src/chart.ts +5 -3
  178. package/src/components/amount-input.tsx +2 -6
  179. package/src/components/calculator.tsx +2 -7
  180. package/src/components/chart-zoom.tsx +4 -47
  181. package/src/components/chip-input.tsx +0 -7
  182. package/src/components/combobox.tsx +1 -1
  183. package/src/components/copy-button.tsx +5 -1
  184. package/src/components/currency-option-list.tsx +8 -42
  185. package/src/components/danger-confirm.tsx +3 -3
  186. package/src/components/data-table-filter-popover.tsx +2 -1
  187. package/src/components/data-table-labels.ts +2 -84
  188. package/src/components/data-table-shared.tsx +55 -2
  189. package/src/components/data-table.tsx +29 -17
  190. package/src/components/dialog-frame.tsx +3 -1
  191. package/src/components/dropdown.tsx +42 -18
  192. package/src/components/entity-combobox.tsx +1 -1
  193. package/src/components/field-base.tsx +0 -11
  194. package/src/components/field-parts.tsx +5 -19
  195. package/src/components/field-strip.tsx +9 -25
  196. package/src/components/field-sync.tsx +0 -7
  197. package/src/components/field.tsx +4 -4
  198. package/src/components/full-bleed-dialog.tsx +4 -3
  199. package/src/components/hover-menu.tsx +12 -16
  200. package/src/components/inline-entity-combobox.tsx +1 -1
  201. package/src/components/modal.tsx +20 -4
  202. package/src/components/multi-select.tsx +13 -30
  203. package/src/components/number-input.tsx +3 -8
  204. package/src/components/popover.tsx +5 -3
  205. package/src/components/series-chart.tsx +1 -1
  206. package/src/components/server-wake.tsx +0 -6
  207. package/src/components/settings-fields.tsx +4 -2
  208. package/src/components/stat-tile.tsx +8 -2
  209. package/src/components/swipeable-row.tsx +15 -37
  210. package/src/components/tooltip.tsx +37 -6
  211. package/src/components/translation-review.tsx +25 -7
  212. package/src/components/use-table-state.ts +91 -10
  213. package/src/components/user-avatar.tsx +2 -2
  214. package/src/components/wizard-stepper.tsx +2 -6
  215. package/src/currencies.ts +70 -0
  216. package/src/demo/demo-banner.tsx +7 -2
  217. package/src/eslint/index.ts +161 -0
  218. package/src/eslint/role-utilities.generated.ts +146 -0
  219. package/src/eslint/role-utilities.ts +99 -0
  220. package/src/feedback/feedback-menu.tsx +2 -2
  221. package/src/feedback/feedback-thread.tsx +3 -8
  222. package/src/hooks/use-dismiss.ts +39 -4
  223. package/src/i18n/kit-labels.tsx +15 -5
  224. package/src/i18n/locales/fr.ts +3 -2
  225. package/src/i18n/locales/it.ts +3 -2
  226. package/src/index.ts +3 -1
  227. package/src/lib/refs.ts +22 -0
  228. package/src/rhf/fields.tsx +10 -10
  229. package/src/rhf/form-core.tsx +5 -4
  230. package/src/rhf/form.tsx +7 -43
  231. package/src/rhf/line-items.tsx +1 -1
  232. package/src/rhf/use-rhf-wizard-step.ts +4 -4
  233. package/src/rhf.ts +3 -2
  234. package/src/shell/option-switcher-menu.tsx +5 -22
  235. package/src/shell/role-switcher.tsx +2 -6
  236. package/src/shell/topbar-action-menu.tsx +7 -24
  237. package/src/shell/topbar-controls.tsx +6 -18
  238. package/src/testing/index.ts +3 -1
  239. package/src/testing/viewport.ts +125 -19
  240. package/src/theme/palette-presets.ts +7 -293
  241. package/src/wizard/types.ts +0 -8
@@ -1,6 +1,7 @@
1
1
  "use client";
2
2
  import { jsx } from "react/jsx-runtime";
3
3
  import { ROW_ACTIONS_KEY } from "./data-table-row-actions.js";
4
+ import { DEFAULT_DATA_TABLE_LABELS } from "./data-table-labels.js";
4
5
  function splitCellProps(props) {
5
6
  if (!props) return { attrs: {} };
6
7
  const {
@@ -32,6 +33,26 @@ function columnLabel(col, labels) {
32
33
  if (col.key === ROW_ACTIONS_KEY && labels) return labels.actions;
33
34
  return col.headerText ?? (typeof col.header === "string" ? col.header : col.key);
34
35
  }
36
+ function resolveDataTableLabels(...layers) {
37
+ if (!layers.some(Boolean)) return DEFAULT_DATA_TABLE_LABELS;
38
+ const partial = {};
39
+ const presets = { ...DEFAULT_DATA_TABLE_LABELS.presets };
40
+ for (const layer of layers) {
41
+ for (const [key, value] of Object.entries(layer ?? {})) {
42
+ if (value === void 0) continue;
43
+ if (key !== "presets") Object.assign(partial, { [key]: value });
44
+ else for (const [preset, text] of Object.entries(value)) if (text !== void 0) presets[preset] = text;
45
+ }
46
+ }
47
+ const merged = { ...DEFAULT_DATA_TABLE_LABELS, ...partial, presets };
48
+ if (partial.columns !== void 0 && partial.columnsCount === void 0) {
49
+ merged.columnsCount = (visible, total) => `${merged.columns} (${visible}/${total})`;
50
+ }
51
+ if (partial.filter !== void 0 && partial.filterColumn === void 0) {
52
+ merged.filterColumn = (column) => `${merged.filter}: ${column}`;
53
+ }
54
+ return merged;
55
+ }
35
56
  function fromOutside(target, row) {
36
57
  return !(target instanceof Node) || !row.contains(target);
37
58
  }
@@ -72,6 +93,7 @@ export {
72
93
  columnLabel,
73
94
  fromOutside,
74
95
  logicalAlign,
96
+ resolveDataTableLabels,
75
97
  splitCellProps
76
98
  };
77
99
  //# sourceMappingURL=data-table-shared.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/components/data-table-shared.tsx"],"sourcesContent":["import type { CSSProperties, ReactNode } from \"react\";\nimport { ROW_ACTIONS_KEY } from \"./data-table-row-actions\";\nimport type { DataTableLabels } from \"./data-table-labels\";\nimport type { DataTableCellProps, DataTableColumn, DataTableHeadProps } from \"./data-table\";\n\n// What DataTable shares with its header (data-table-head.tsx), its column panel and its\n// phone layout (data-table-mobile.tsx): the cell-attribute and alignment rules, a\n// column's plain-text name, and the row link. Moved out of data-table.tsx unchanged\n// (refactor audit 2026-10, MOD-3), into a module of their own so none of those has to\n// import data-table.tsx at run time — which imports them.\n\n/**\n * Split a caller's cell attributes into what may reach the element and what the kit\n * owns. `role` and `data-col` are dropped outright (see `cellProps`); `className` and\n * `style` come back separately so the kit can merge rather than replace them.\n */\nexport function splitCellProps(\n props: DataTableCellProps | DataTableHeadProps | undefined,\n): { attrs: Record<string, unknown>; className?: string; style?: CSSProperties } {\n if (!props) return { attrs: {} };\n const {\n className,\n style,\n role: _role,\n \"data-col\": _col,\n ...attrs\n } = props as DataTableCellProps & { \"data-col\"?: unknown };\n return { attrs, className, style };\n}\n\n// A bare `text-right` / `text-left` token, with or without variant prefixes\n// (`md:text-right`). Deliberately NOT `text-right-…` or an arbitrary value.\nconst PHYSICAL_ALIGN = /(^|\\s)((?:[\\w-]+:)*)text-(left|right)(?=\\s|$)/g;\n\n/**\n * A column's `className`/`headClassName` with physical text alignment rewritten to\n * logical: `text-right` → `text-end`, `text-left` → `text-start`.\n *\n * Every numeric column written before 0.7.0 says `text-right`, and that is what it\n * MEANT: the end of the line, where digits line up. Rendered as written, a\n * right-to-left table puts those numbers on the far side from their own header's\n * sort button. Rewriting keeps each existing call site correct in both directions\n * with no change on its side; a caller who genuinely wants the physical side in RTL\n * too can say `ltr:text-right rtl:text-right`, which this leaves alone.\n */\nexport function logicalAlign(className: string | undefined): string | undefined {\n return className?.replace(\n PHYSICAL_ALIGN,\n (token: string, lead: string, variants: string, side: string) =>\n // Already scoped to one direction: that is a caller choosing the physical side.\n /(^|:)(ltr|rtl):/.test(variants)\n ? token\n : `${lead}${variants}text-${side === \"right\" ? \"end\" : \"start\"}`,\n );\n}\n\n/** Drop the undefined entries of a {@link DataTableProps.rowAttributes} map, so a\n * caller can write `{ \"data-x\": cond ? \"y\" : undefined }` and get no attribute\n * at all rather than the string \"undefined\" in the DOM. */\nexport function cleanAttrs(\n attrs: Record<string, string | undefined> | undefined,\n): Record<string, string> | undefined {\n if (!attrs) return undefined;\n const out: Record<string, string> = {};\n for (const [k, v] of Object.entries(attrs)) if (v !== undefined) out[k] = v;\n return out;\n}\n\n/**\n * A column's name as a plain string.\n *\n * `header` is a ReactNode — an icon, a unit suffix, a tooltip-wrapped span — and\n * neither a live region nor the column-settings checklist can speak a React element.\n * The `key` fallback is the one the settings panel has always used; a column whose\n * header is not a string and whose key is not human-readable is a call site that\n * should pass a string header, and that is visible in what gets announced.\n */\nexport function columnLabel<T>(col: DataTableColumn<T>, labels?: DataTableLabels): string {\n if (col.key === ROW_ACTIONS_KEY && labels) return labels.actions;\n return col.headerText ?? (typeof col.header === \"string\" ? col.header : col.key);\n}\n\n/**\n * Whether a click that reached `row`'s handler did not start in the row's own DOM.\n *\n * React bubbles an event through the COMPONENT tree, portals included: a popover, a\n * menu or a dialog a cell (or the expansion under it) opens renders at `<body>` but\n * still reports its clicks to every React ancestor — the row among them. So a click\n * in a cell's portalled date picker opened the row it came from, and a page wrapped\n * such content in `<div onClick={(e) => e.stopPropagation()}>` to stop it (kastlan's\n * feedback page did, round its comments). The row owns the clicks on the row.\n */\nexport function fromOutside(target: EventTarget | null, row: HTMLElement): boolean {\n return !(target instanceof Node) || !row.contains(target);\n}\n\n/** A click the APP owns. Anything else — middle, ⌘/Ctrl, Shift, Alt — belongs to\n * the browser, and the whole feature is not touching it. */\nconst isPlainLeftClick = (e: React.MouseEvent) =>\n e.button === 0 && !e.metaKey && !e.ctrlKey && !e.shiftKey && !e.altKey;\n\n/**\n * The anchor a linkable row is opened through (Keksdose feedback #451).\n *\n * The plain left click is OURS: cancelled, then handed to the row's own handler, so\n * clicking a row still expands it in place instead of reloading the page. Every\n * other click — middle, ⌘/Ctrl, Shift, Alt — is left completely untouched, which is\n * the entire point of using an anchor rather than an `onAuxClick` + `window.open`:\n * that imitation buys back the middle click and still loses ⌘/Ctrl-click, \"copy link\n * address\", the status-bar preview and the phone's long-press menu.\n *\n * Modified clicks DO stop propagating: on a table with `selection`, ⌘/Ctrl-click and\n * Shift-click on a row mean \"select\" (feedback #289), and a row must not change its\n * selection while the browser is opening a tab. The rest of the row still selects —\n * the link is one cell, not the row.\n *\n * `draggable={false}` because an anchor otherwise hijacks a horizontal mouse drag as\n * a link-drag: that is both drag-to-select-text on the desktop table and the mouse\n * path through SwipeableRow's gesture on the mobile card.\n */\nexport function RowLink({\n href,\n onActivate,\n className,\n children,\n ...rest\n}: {\n href: string;\n /** Runs on a plain left click. Omit it where an ANCESTOR already handles the\n * click (the desktop `<tr>`), or the row would toggle twice. */\n onActivate?: () => void;\n className?: string;\n children: ReactNode;\n} & Omit<\n React.AnchorHTMLAttributes<HTMLAnchorElement>,\n \"href\" | \"className\" | \"children\" | \"onClick\" | \"onClickCapture\"\n>) {\n return (\n <a\n {...rest}\n href={href}\n draggable={false}\n className={className}\n // CAPTURE phase, and it has to be: on the mobile card the whole card is the\n // anchor, so a nested control sits INSIDE it — and the ones that matter\n // (the feedback row's status toggles) stop the click propagating so the row\n // won't also expand. A bubble-phase handler would never run for those, and\n // the browser would follow the link out from under the button press. Cancelling\n // here happens before any descendant can silence the event; `stopPropagation`\n // does not undo a `preventDefault`.\n onClickCapture={(e) => {\n // A click in a portal a descendant opened is not a click on the link: leave\n // it (and a link inside it) alone. See `fromOutside`.\n if (fromOutside(e.target, e.currentTarget)) return;\n if (isPlainLeftClick(e)) e.preventDefault();\n }}\n onClick={(e) => {\n if (fromOutside(e.target, e.currentTarget)) return;\n if (!isPlainLeftClick(e)) {\n e.stopPropagation();\n return;\n }\n // Already cancelled above; this phase only decides what opens. A nested\n // control that stopped propagation deliberately never reaches it, which is\n // exactly how the row behaved before it became a link.\n onActivate?.();\n }}\n >\n {children}\n </a>\n );\n}\n"],"mappings":";AA0II;AAzIJ,SAAS,uBAAuB;AAezB,SAAS,eACd,OAC+E;AAC/E,MAAI,CAAC,MAAO,QAAO,EAAE,OAAO,CAAC,EAAE;AAC/B,QAAM;AAAA,IACJ;AAAA,IACA;AAAA,IACA,MAAM;AAAA,IACN,YAAY;AAAA,IACZ,GAAG;AAAA,EACL,IAAI;AACJ,SAAO,EAAE,OAAO,WAAW,MAAM;AACnC;AAIA,MAAM,iBAAiB;AAahB,SAAS,aAAa,WAAmD;AAC9E,SAAO,WAAW;AAAA,IAChB;AAAA,IACA,CAAC,OAAe,MAAc,UAAkB;AAAA;AAAA,MAE9C,kBAAkB,KAAK,QAAQ,IAC3B,QACA,GAAG,IAAI,GAAG,QAAQ,QAAQ,SAAS,UAAU,QAAQ,OAAO;AAAA;AAAA,EACpE;AACF;AAKO,SAAS,WACd,OACoC;AACpC,MAAI,CAAC,MAAO,QAAO;AACnB,QAAM,MAA8B,CAAC;AACrC,aAAW,CAAC,GAAG,CAAC,KAAK,OAAO,QAAQ,KAAK,EAAG,KAAI,MAAM,OAAW,KAAI,CAAC,IAAI;AAC1E,SAAO;AACT;AAWO,SAAS,YAAe,KAAyB,QAAkC;AACxF,MAAI,IAAI,QAAQ,mBAAmB,OAAQ,QAAO,OAAO;AACzD,SAAO,IAAI,eAAe,OAAO,IAAI,WAAW,WAAW,IAAI,SAAS,IAAI;AAC9E;AAYO,SAAS,YAAY,QAA4B,KAA2B;AACjF,SAAO,EAAE,kBAAkB,SAAS,CAAC,IAAI,SAAS,MAAM;AAC1D;AAIA,MAAM,mBAAmB,CAAC,MACxB,EAAE,WAAW,KAAK,CAAC,EAAE,WAAW,CAAC,EAAE,WAAW,CAAC,EAAE,YAAY,CAAC,EAAE;AAqB3D,SAAS,QAAQ;AAAA,EACtB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,GAAG;AACL,GAUG;AACD,SACE;AAAA,IAAC;AAAA;AAAA,MACE,GAAG;AAAA,MACJ;AAAA,MACA,WAAW;AAAA,MACX;AAAA,MAQA,gBAAgB,CAAC,MAAM;AAGrB,YAAI,YAAY,EAAE,QAAQ,EAAE,aAAa,EAAG;AAC5C,YAAI,iBAAiB,CAAC,EAAG,GAAE,eAAe;AAAA,MAC5C;AAAA,MACA,SAAS,CAAC,MAAM;AACd,YAAI,YAAY,EAAE,QAAQ,EAAE,aAAa,EAAG;AAC5C,YAAI,CAAC,iBAAiB,CAAC,GAAG;AACxB,YAAE,gBAAgB;AAClB;AAAA,QACF;AAIA,qBAAa;AAAA,MACf;AAAA,MAEC;AAAA;AAAA,EACH;AAEJ;","names":[]}
1
+ {"version":3,"sources":["../../src/components/data-table-shared.tsx"],"sourcesContent":["import type { CSSProperties, ReactNode } from \"react\";\nimport { ROW_ACTIONS_KEY } from \"./data-table-row-actions\";\nimport { DEFAULT_DATA_TABLE_LABELS, type DataTableLabels } from \"./data-table-labels\";\nimport type { DataTableCellProps, DataTableColumn, DataTableHeadProps } from \"./data-table\";\nimport type { LabelOverride } from \"../i18n/kit-labels\";\n\n// What DataTable shares with its header (data-table-head.tsx), its column panel and its\n// phone layout (data-table-mobile.tsx): the cell-attribute and alignment rules, a\n// column's plain-text name, and the row link. Moved out of data-table.tsx unchanged\n// (refactor audit 2026-10, MOD-3), into a module of their own so none of those has to\n// import data-table.tsx at run time — which imports them. Since 0.35 also the table's\n// label resolver, which DataTable and its FilterPopover share and the barrel no longer\n// exports (round 0.35 R1).\n\n/**\n * Split a caller's cell attributes into what may reach the element and what the kit\n * owns. `role` and `data-col` are dropped outright (see `cellProps`); `className` and\n * `style` come back separately so the kit can merge rather than replace them.\n */\nexport function splitCellProps(\n props: DataTableCellProps | DataTableHeadProps | undefined,\n): { attrs: Record<string, unknown>; className?: string; style?: CSSProperties } {\n if (!props) return { attrs: {} };\n const {\n className,\n style,\n role: _role,\n \"data-col\": _col,\n ...attrs\n } = props as DataTableCellProps & { \"data-col\"?: unknown };\n return { attrs, className, style };\n}\n\n// A bare `text-right` / `text-left` token, with or without variant prefixes\n// (`md:text-right`). Deliberately NOT `text-right-…` or an arbitrary value.\nconst PHYSICAL_ALIGN = /(^|\\s)((?:[\\w-]+:)*)text-(left|right)(?=\\s|$)/g;\n\n/**\n * A column's `className`/`headClassName` with physical text alignment rewritten to\n * logical: `text-right` → `text-end`, `text-left` → `text-start`.\n *\n * Every numeric column written before 0.7.0 says `text-right`, and that is what it\n * MEANT: the end of the line, where digits line up. Rendered as written, a\n * right-to-left table puts those numbers on the far side from their own header's\n * sort button. Rewriting keeps each existing call site correct in both directions\n * with no change on its side; a caller who genuinely wants the physical side in RTL\n * too can say `ltr:text-right rtl:text-right`, which this leaves alone.\n */\nexport function logicalAlign(className: string | undefined): string | undefined {\n return className?.replace(\n PHYSICAL_ALIGN,\n (token: string, lead: string, variants: string, side: string) =>\n // Already scoped to one direction: that is a caller choosing the physical side.\n /(^|:)(ltr|rtl):/.test(variants)\n ? token\n : `${lead}${variants}text-${side === \"right\" ? \"end\" : \"start\"}`,\n );\n}\n\n/** Drop the undefined entries of a {@link DataTableProps.rowAttributes} map, so a\n * caller can write `{ \"data-x\": cond ? \"y\" : undefined }` and get no attribute\n * at all rather than the string \"undefined\" in the DOM. */\nexport function cleanAttrs(\n attrs: Record<string, string | undefined> | undefined,\n): Record<string, string> | undefined {\n if (!attrs) return undefined;\n const out: Record<string, string> = {};\n for (const [k, v] of Object.entries(attrs)) if (v !== undefined) out[k] = v;\n return out;\n}\n\n/**\n * A column's name as a plain string.\n *\n * `header` is a ReactNode — an icon, a unit suffix, a tooltip-wrapped span — and\n * neither a live region nor the column-settings checklist can speak a React element.\n * The `key` fallback is the one the settings panel has always used; a column whose\n * header is not a string and whose key is not human-readable is a call site that\n * should pass a string header, and that is visible in what gets announced.\n */\nexport function columnLabel<T>(col: DataTableColumn<T>, labels?: DataTableLabels): string {\n if (col.key === ROW_ACTIONS_KEY && labels) return labels.actions;\n return col.headerText ?? (typeof col.header === \"string\" ? col.header : col.key);\n}\n\n/**\n * The table's labels: caller overrides merged onto the English defaults (presets merged\n * too, key by key, as the provider merges them — so the parameter is a\n * {@link LabelOverride}).\n *\n * DataTable's own, and FilterPopover's: public until 0.35, when it left the barrel for\n * DataTable's `labels` and the provider's `dataTable`, which it serves (round 0.35 R1,\n * refactor audit 2026-10 API-6a). It stays a resolver rather than `useKitLabels` for the\n * two keys it derives (`columnsCount`, `filterColumn`), which a plain merge would not.\n *\n * Lenient on purpose — a table with no labels at all still renders — so a translated app\n * finds out what fell back to English from `missingKitLabels(labels,\n * DEFAULT_UI_KIT_LABELS)` in its own test, where the answer is actionable.\n */\nexport function resolveDataTableLabels(\n ...layers: (LabelOverride<DataTableLabels> | undefined)[]\n): DataTableLabels {\n if (!layers.some(Boolean)) return DEFAULT_DATA_TABLE_LABELS;\n // The layers in order, the provider's then the prop's, later winning key by key. An\n // `undefined` key is skipped, as `useKitLabels` skips it, rather than blanking the word\n // under it: DataTable spread `{ ...provider, ...prop }`, so `labels={{ autoSize:\n // undefined }}` blanked the provider's word and the English one (round 0.35). `presets`\n // is a record and is merged the same way, so renaming one keeps the other ten.\n const partial: Partial<Omit<DataTableLabels, \"presets\">> = {};\n const presets = { ...DEFAULT_DATA_TABLE_LABELS.presets };\n for (const layer of layers) {\n for (const [key, value] of Object.entries(layer ?? {})) {\n if (value === undefined) continue;\n if (key !== \"presets\") Object.assign(partial, { [key]: value });\n else for (const [preset, text] of Object.entries(value)) if (text !== undefined) presets[preset] = text;\n }\n }\n const merged: DataTableLabels = { ...DEFAULT_DATA_TABLE_LABELS, ...partial, presets };\n // `columnsCount` was split out of `columns` + a template literal. Consumers have\n // been translating `columns` since 0.1, and backfilling the English default over\n // that would hand them \"Columns (4/9)\" back for having translated the key that\n // existed at the time. So the count is derived from the RESOLVED `columns` unless the\n // caller states the whole sentence, which is the escape hatch for a language where\n // the parenthetical does not fit.\n if (partial.columns !== undefined && partial.columnsCount === undefined) {\n merged.columnsCount = (visible, total) => `${merged.columns} (${visible}/${total})`;\n }\n // The same for `filterColumn` (0.34), from a translated `filter`: the app's own word\n // with the column, rather than English for having translated the key that existed.\n if (partial.filter !== undefined && partial.filterColumn === undefined) {\n merged.filterColumn = (column) => `${merged.filter}: ${column}`;\n }\n return merged;\n}\n\n/**\n * Whether a click that reached `row`'s handler did not start in the row's own DOM.\n *\n * React bubbles an event through the COMPONENT tree, portals included: a popover, a\n * menu or a dialog a cell (or the expansion under it) opens renders at `<body>` but\n * still reports its clicks to every React ancestor — the row among them. So a click\n * in a cell's portalled date picker opened the row it came from, and a page wrapped\n * such content in `<div onClick={(e) => e.stopPropagation()}>` to stop it (kastlan's\n * feedback page did, round its comments). The row owns the clicks on the row.\n */\nexport function fromOutside(target: EventTarget | null, row: HTMLElement): boolean {\n return !(target instanceof Node) || !row.contains(target);\n}\n\n/** A click the APP owns. Anything else — middle, ⌘/Ctrl, Shift, Alt — belongs to\n * the browser, and the whole feature is not touching it. */\nconst isPlainLeftClick = (e: React.MouseEvent) =>\n e.button === 0 && !e.metaKey && !e.ctrlKey && !e.shiftKey && !e.altKey;\n\n/**\n * The anchor a linkable row is opened through (Keksdose feedback #451).\n *\n * The plain left click is OURS: cancelled, then handed to the row's own handler, so\n * clicking a row still expands it in place instead of reloading the page. Every\n * other click — middle, ⌘/Ctrl, Shift, Alt — is left completely untouched, which is\n * the entire point of using an anchor rather than an `onAuxClick` + `window.open`:\n * that imitation buys back the middle click and still loses ⌘/Ctrl-click, \"copy link\n * address\", the status-bar preview and the phone's long-press menu.\n *\n * Modified clicks DO stop propagating: on a table with `selection`, ⌘/Ctrl-click and\n * Shift-click on a row mean \"select\" (feedback #289), and a row must not change its\n * selection while the browser is opening a tab. The rest of the row still selects —\n * the link is one cell, not the row.\n *\n * `draggable={false}` because an anchor otherwise hijacks a horizontal mouse drag as\n * a link-drag: that is both drag-to-select-text on the desktop table and the mouse\n * path through SwipeableRow's gesture on the mobile card.\n */\nexport function RowLink({\n href,\n onActivate,\n className,\n children,\n ...rest\n}: {\n href: string;\n /** Runs on a plain left click. Omit it where an ANCESTOR already handles the\n * click (the desktop `<tr>`), or the row would toggle twice. */\n onActivate?: () => void;\n className?: string;\n children: ReactNode;\n} & Omit<\n React.AnchorHTMLAttributes<HTMLAnchorElement>,\n \"href\" | \"className\" | \"children\" | \"onClick\" | \"onClickCapture\"\n>) {\n return (\n <a\n {...rest}\n href={href}\n draggable={false}\n className={className}\n // CAPTURE phase, and it has to be: on the mobile card the whole card is the\n // anchor, so a nested control sits INSIDE it — and the ones that matter\n // (the feedback row's status toggles) stop the click propagating so the row\n // won't also expand. A bubble-phase handler would never run for those, and\n // the browser would follow the link out from under the button press. Cancelling\n // here happens before any descendant can silence the event; `stopPropagation`\n // does not undo a `preventDefault`.\n onClickCapture={(e) => {\n // A click in a portal a descendant opened is not a click on the link: leave\n // it (and a link inside it) alone. See `fromOutside`.\n if (fromOutside(e.target, e.currentTarget)) return;\n if (isPlainLeftClick(e)) e.preventDefault();\n }}\n onClick={(e) => {\n if (fromOutside(e.target, e.currentTarget)) return;\n if (!isPlainLeftClick(e)) {\n e.stopPropagation();\n return;\n }\n // Already cancelled above; this phase only decides what opens. A nested\n // control that stopped propagation deliberately never reaches it, which is\n // exactly how the row behaved before it became a link.\n onActivate?.();\n }}\n >\n {children}\n </a>\n );\n}\n"],"mappings":"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","names":[]}
@@ -262,6 +262,16 @@ export interface DataTableProps<T> {
262
262
  onFiltersChange?: (next: FilterState) => void;
263
263
  sorts?: SortState[];
264
264
  onSortsChange?: (next: SortState[]) => void;
265
+ /**
266
+ * Controlled page size, as `sorts` above: the table pages by it and hands its pager's
267
+ * choice to `onPageSizeChange` (`Infinity` is "All"). For tables that page ALIKE —
268
+ * `TranslationReviewPanel`'s groups share one page size as they share one sort
269
+ * (0.35). Left out, the table keeps its own, from `defaultPageSize`, the URL and
270
+ * `storageKey`. A change of value sends a client-side table back to page 1. In
271
+ * `serverPagination` mode the page size is `serverPagination.pageSize`.
272
+ */
273
+ pageSize?: number;
274
+ onPageSizeChange?: (pageSize: number) => void;
265
275
  /**
266
276
  * Opt-in multi-row selection (desktop table only). When provided, a leading
267
277
  * checkbox column is rendered with a select-all box in the header; the owner
@@ -524,7 +534,7 @@ export interface DataTableProps<T> {
524
534
  * On a phone it heads the summary card, which is also named by it.
525
535
  *
526
536
  * There is no built-in default word: a new kit label key is a silent English string
527
- * in every translated app (see `missingDataTableLabels`), and the caller has the
537
+ * in every translated app (see `missingKitLabels`), and the caller has the
528
538
  * word in its own catalogue already. Without it the row is unlabelled on screen —
529
539
  * pass one.
530
540
  */
@@ -600,4 +610,4 @@ export interface DataTableSortLabels {
600
610
  * (DataTableLabels extends DataTableSortLabels; one source for the English). */
601
611
  export declare const DEFAULT_DATA_TABLE_SORT_LABELS: DataTableSortLabels;
602
612
  import { type TableUrlSync } from "./use-table-state.js";
603
- export declare function DataTable<T>({ rows, columns: columnsProp, rowKey, defaultPageSize, onRowClick, rowHref, expandedRow, isExpanded, rowClassName, rowAttributes, paginated, empty, storageKey, urlSync, fillHeight, maxBodyHeight, serverPagination, filters: filtersProp, onFiltersChange, sorts: sortsProp, onSortsChange, selection, labels: labelsProp, locale: localeProp, storageKeyPrefix: storageKeyPrefixProp, mobileExpandAsDialog, mobileDetailsInRow, rowName, mobileDialogBackCloses, mobileGroupBy, mobileGroupLabel, mobileCard, mobileSort, mobileSwipeActions, leadingRow, density, sortCycle, chrome, columnSettings: columnSettingsProp, resizable: resizableProp, multiSort: multiSortProp, frame, edgeFade, className, rowActions, toolbar, footerLabel, footer: footerProp, }: DataTableProps<T>): import("react").JSX.Element;
613
+ export declare function DataTable<T>({ rows, columns: columnsProp, rowKey, defaultPageSize, onRowClick, rowHref, expandedRow, isExpanded, rowClassName, rowAttributes, paginated, empty, storageKey, urlSync, fillHeight, maxBodyHeight, serverPagination, filters: filtersProp, onFiltersChange, sorts: sortsProp, onSortsChange, pageSize: pageSizeProp, onPageSizeChange, selection, labels: labelsProp, locale: localeProp, storageKeyPrefix: storageKeyPrefixProp, mobileExpandAsDialog, mobileDetailsInRow, rowName, mobileDialogBackCloses, mobileGroupBy, mobileGroupLabel, mobileCard, mobileSort, mobileSwipeActions, leadingRow, density, sortCycle, chrome, columnSettings: columnSettingsProp, resizable: resizableProp, multiSort: multiSortProp, frame, edgeFade, className, rowActions, toolbar, footerLabel, footer: footerProp, }: DataTableProps<T>): import("react").JSX.Element;
@@ -7,7 +7,15 @@ import { Spinner } from "./spinner.js";
7
7
  import { ROW_ACTIONS_KEY, useRowActionColumn } from "./data-table-row-actions.js";
8
8
  import { useColumnResize } from "./data-table-resize.js";
9
9
  import { useRangeSelection } from "./data-table-selection.js";
10
- import { RowLink, cleanAttrs, columnLabel, fromOutside, logicalAlign, splitCellProps } from "./data-table-shared.js";
10
+ import {
11
+ RowLink,
12
+ cleanAttrs,
13
+ columnLabel,
14
+ fromOutside,
15
+ logicalAlign,
16
+ resolveDataTableLabels,
17
+ splitCellProps
18
+ } from "./data-table-shared.js";
11
19
  import { MobileFilters, MobileRow, MobileSort, StackedFields } from "./data-table-mobile.js";
12
20
  import { DataTableHead } from "./data-table-head.js";
13
21
  import { ColumnSettingsPanel } from "./data-table-column-settings.js";
@@ -19,7 +27,7 @@ import { Pagination } from "./data-table-pagination.js";
19
27
  import { FullBleedDialog } from "./full-bleed-dialog.js";
20
28
  import { usePhoneLayout } from "../hooks/use-breakpoint.js";
21
29
  import { useAnnounce } from "../hooks/use-announce.js";
22
- import { DEFAULT_DATA_TABLE_LABELS, resolveDataTableLabels } from "./data-table-labels.js";
30
+ import { DEFAULT_DATA_TABLE_LABELS } from "./data-table-labels.js";
23
31
  import { useKitLabelOverrides, useKitLocale, useKitTableStorageKeyPrefix } from "../i18n/kit-labels.js";
24
32
  import { useEdgeFade } from "../lib/strip-fade.js";
25
33
  import { dirOf } from "../lib/direction.js";
@@ -74,6 +82,8 @@ function DataTable({
74
82
  onFiltersChange,
75
83
  sorts: sortsProp,
76
84
  onSortsChange,
85
+ pageSize: pageSizeProp,
86
+ onPageSizeChange,
77
87
  selection,
78
88
  labels: labelsProp,
79
89
  locale: localeProp,
@@ -112,16 +122,7 @@ function DataTable({
112
122
  const labelOverrides = useKitLabelOverrides("dataTable");
113
123
  const providerStorageKeyPrefix = useKitTableStorageKeyPrefix();
114
124
  const storageKeyPrefix = storageKeyPrefixProp ?? providerStorageKeyPrefix ?? DEFAULT_PERSIST_PREFIX;
115
- const labels = useMemo(
116
- () => resolveDataTableLabels(
117
- labelOverrides || labelsProp ? {
118
- ...labelOverrides,
119
- ...labelsProp,
120
- presets: { ...labelOverrides?.presets, ...labelsProp?.presets }
121
- } : void 0
122
- ),
123
- [labelOverrides, labelsProp]
124
- );
125
+ const labels = useMemo(() => resolveDataTableLabels(labelOverrides, labelsProp), [labelOverrides, labelsProp]);
125
126
  const locale = useKitLocale(localeProp);
126
127
  const { columns, swipesFromActions } = useRowActionColumn({
127
128
  columns: columnsProp,
@@ -155,7 +156,9 @@ function DataTable({
155
156
  urlSync,
156
157
  defaultPageSize,
157
158
  sortsProp,
158
- filtersProp
159
+ filtersProp,
160
+ // Ignored in `serverPagination` mode, where the owner's is the page size.
161
+ pageSizeProp: isServer ? void 0 : pageSizeProp
159
162
  });
160
163
  const widths = useMemo(() => resizable ? storedWidths : {}, [resizable, storedWidths]);
161
164
  const hiddenCols = useMemo(
@@ -817,7 +820,8 @@ function DataTable({
817
820
  total: paginationTotal,
818
821
  onPage: goToPage,
819
822
  onPageSize: isServer ? serverPagination.onPageSizeChange : (n) => {
820
- setPageSize(n);
823
+ if (onPageSizeChange) onPageSizeChange(n);
824
+ else setPageSize(n);
821
825
  setPage(0);
822
826
  },
823
827
  labels,