@lerianstudio/sindarian-ui 2.0.0-beta.2 → 2.0.0-beta.20

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 (211) hide show
  1. package/LICENSE +93 -0
  2. package/README.md +52 -1
  3. package/dist/components/breadcrumb/index.d.ts.map +1 -1
  4. package/dist/components/breadcrumb/index.js +25 -1
  5. package/dist/components/card/account-card/index.js +5 -5
  6. package/dist/components/card/basic-card/index.d.ts.map +1 -1
  7. package/dist/components/card/basic-card/index.js +2 -2
  8. package/dist/components/card/entity-card/index.js +1 -1
  9. package/dist/components/dialog/confirmation-dialog/index.d.ts.map +1 -1
  10. package/dist/components/dialog/confirmation-dialog/index.js +4 -1
  11. package/dist/components/entity-box/index.d.ts +18 -2
  12. package/dist/components/entity-box/index.d.ts.map +1 -1
  13. package/dist/components/entity-box/index.js +32 -4
  14. package/dist/components/form/date-picker-field/index.js +1 -1
  15. package/dist/components/form/date-range-field/index.js +1 -1
  16. package/dist/components/form/date-time-picker-field/index.js +1 -1
  17. package/dist/components/form/password-field/index.d.ts +5 -1
  18. package/dist/components/form/password-field/index.d.ts.map +1 -1
  19. package/dist/components/form/password-field/index.js +2 -2
  20. package/dist/components/form/select-field/index.d.ts +15 -1
  21. package/dist/components/form/select-field/index.d.ts.map +1 -1
  22. package/dist/components/form/select-field/index.js +28 -3
  23. package/dist/components/form/switch-field/index.d.ts.map +1 -1
  24. package/dist/components/form/switch-field/index.js +6 -1
  25. package/dist/components/page/index.d.ts +35 -1
  26. package/dist/components/page/index.d.ts.map +1 -1
  27. package/dist/components/page/index.js +6 -2
  28. package/dist/components/page-header/index.js +1 -1
  29. package/dist/components/table/id-table-cell.d.ts +13 -1
  30. package/dist/components/table/id-table-cell.d.ts.map +1 -1
  31. package/dist/components/table/id-table-cell.js +99 -9
  32. package/dist/components/table/index.d.ts +1 -0
  33. package/dist/components/table/index.d.ts.map +1 -1
  34. package/dist/components/table/index.js +1 -0
  35. package/dist/components/table/locked-table-actions.d.ts +24 -0
  36. package/dist/components/table/locked-table-actions.d.ts.map +1 -1
  37. package/dist/components/table/locked-table-actions.js +31 -1
  38. package/dist/components/table/name-table-cell.d.ts +57 -4
  39. package/dist/components/table/name-table-cell.d.ts.map +1 -1
  40. package/dist/components/table/name-table-cell.js +50 -2
  41. package/dist/components/ui/autocomplete/index.d.ts +24 -0
  42. package/dist/components/ui/autocomplete/index.d.ts.map +1 -1
  43. package/dist/components/ui/autocomplete/index.js +24 -0
  44. package/dist/components/ui/badge/index.d.ts +11 -7
  45. package/dist/components/ui/badge/index.d.ts.map +1 -1
  46. package/dist/components/ui/badge/index.js +20 -8
  47. package/dist/components/ui/button/index.d.ts +3 -3
  48. package/dist/components/ui/copy-field/index.d.ts +25 -1
  49. package/dist/components/ui/copy-field/index.d.ts.map +1 -1
  50. package/dist/components/ui/copy-field/index.js +16 -8
  51. package/dist/components/ui/dialog/index.d.ts +1 -1
  52. package/dist/components/ui/dialog/index.d.ts.map +1 -1
  53. package/dist/components/ui/dialog/index.js +2 -2
  54. package/dist/components/ui/file-upload/index.d.ts +159 -0
  55. package/dist/components/ui/file-upload/index.d.ts.map +1 -1
  56. package/dist/components/ui/file-upload/index.js +308 -6
  57. package/dist/components/ui/icon-button/index.d.ts +1 -1
  58. package/dist/components/ui/icon-button/styles.css +42 -0
  59. package/dist/components/ui/input/styles.css +79 -4
  60. package/dist/components/ui/otp-input/index.js +1 -1
  61. package/dist/components/ui/radio-group/index.js +1 -1
  62. package/dist/components/ui/select/index.d.ts.map +1 -1
  63. package/dist/components/ui/select/index.js +8 -4
  64. package/dist/components/ui/select/styles.css +1 -1
  65. package/dist/components/ui/sheet/index.d.ts +42 -0
  66. package/dist/components/ui/sheet/index.d.ts.map +1 -1
  67. package/dist/components/ui/sheet/index.js +47 -14
  68. package/dist/components/ui/sidebar/index.d.ts +1 -0
  69. package/dist/components/ui/sidebar/index.d.ts.map +1 -1
  70. package/dist/components/ui/sidebar/index.js +1 -0
  71. package/dist/components/ui/sidebar/sidebar-expand-button.d.ts +1 -1
  72. package/dist/components/ui/sidebar/sidebar-expand-button.d.ts.map +1 -1
  73. package/dist/components/ui/sidebar/sidebar-expand-button.js +15 -1
  74. package/dist/components/ui/sidebar/sidebar-item.d.ts +1 -1
  75. package/dist/components/ui/sidebar/sidebar-item.d.ts.map +1 -1
  76. package/dist/components/ui/sidebar/sidebar-item.js +28 -4
  77. package/dist/components/ui/sidebar/sidebar-provider.d.ts +58 -0
  78. package/dist/components/ui/sidebar/sidebar-provider.d.ts.map +1 -1
  79. package/dist/components/ui/sidebar/sidebar-provider.js +146 -4
  80. package/dist/components/ui/sidebar/sidebar-root.d.ts +38 -1
  81. package/dist/components/ui/sidebar/sidebar-root.d.ts.map +1 -1
  82. package/dist/components/ui/sidebar/sidebar-root.js +111 -5
  83. package/dist/components/ui/sidebar/sidebar-trigger.d.ts +45 -0
  84. package/dist/components/ui/sidebar/sidebar-trigger.d.ts.map +1 -0
  85. package/dist/components/ui/sidebar/sidebar-trigger.js +57 -0
  86. package/dist/components/ui/textarea/index.js +1 -1
  87. package/dist/components/ui/toast/toaster.d.ts +5 -1
  88. package/dist/components/ui/toast/toaster.d.ts.map +1 -1
  89. package/dist/components/ui/toast/toaster.js +3 -2
  90. package/dist/enterprise/data-table/index.d.ts +77 -7
  91. package/dist/enterprise/data-table/index.d.ts.map +1 -1
  92. package/dist/enterprise/data-table/index.js +56 -11
  93. package/dist/enterprise/date-range-picker/index.js +4 -4
  94. package/dist/enterprise/number-input/index.d.ts.map +1 -1
  95. package/dist/enterprise/number-input/index.js +1 -1
  96. package/dist/esm/components/breadcrumb/index.d.ts.map +1 -1
  97. package/dist/esm/components/breadcrumb/index.js +25 -1
  98. package/dist/esm/components/card/account-card/index.js +5 -5
  99. package/dist/esm/components/card/basic-card/index.d.ts.map +1 -1
  100. package/dist/esm/components/card/basic-card/index.js +2 -2
  101. package/dist/esm/components/card/entity-card/index.js +1 -1
  102. package/dist/esm/components/dialog/confirmation-dialog/index.d.ts.map +1 -1
  103. package/dist/esm/components/dialog/confirmation-dialog/index.js +4 -1
  104. package/dist/esm/components/entity-box/index.d.ts +18 -2
  105. package/dist/esm/components/entity-box/index.d.ts.map +1 -1
  106. package/dist/esm/components/entity-box/index.js +29 -4
  107. package/dist/esm/components/form/date-picker-field/index.js +1 -1
  108. package/dist/esm/components/form/date-range-field/index.js +1 -1
  109. package/dist/esm/components/form/date-time-picker-field/index.js +1 -1
  110. package/dist/esm/components/form/password-field/index.d.ts +5 -1
  111. package/dist/esm/components/form/password-field/index.d.ts.map +1 -1
  112. package/dist/esm/components/form/password-field/index.js +2 -2
  113. package/dist/esm/components/form/select-field/index.d.ts +15 -1
  114. package/dist/esm/components/form/select-field/index.d.ts.map +1 -1
  115. package/dist/esm/components/form/select-field/index.js +29 -4
  116. package/dist/esm/components/form/switch-field/index.d.ts.map +1 -1
  117. package/dist/esm/components/form/switch-field/index.js +7 -2
  118. package/dist/esm/components/page/index.d.ts +35 -1
  119. package/dist/esm/components/page/index.d.ts.map +1 -1
  120. package/dist/esm/components/page/index.js +6 -2
  121. package/dist/esm/components/page-header/index.js +1 -1
  122. package/dist/esm/components/table/id-table-cell.d.ts +13 -1
  123. package/dist/esm/components/table/id-table-cell.d.ts.map +1 -1
  124. package/dist/esm/components/table/id-table-cell.js +100 -7
  125. package/dist/esm/components/table/index.d.ts +1 -0
  126. package/dist/esm/components/table/index.d.ts.map +1 -1
  127. package/dist/esm/components/table/index.js +1 -0
  128. package/dist/esm/components/table/locked-table-actions.d.ts +24 -0
  129. package/dist/esm/components/table/locked-table-actions.d.ts.map +1 -1
  130. package/dist/esm/components/table/locked-table-actions.js +31 -1
  131. package/dist/esm/components/table/name-table-cell.d.ts +57 -4
  132. package/dist/esm/components/table/name-table-cell.d.ts.map +1 -1
  133. package/dist/esm/components/table/name-table-cell.js +50 -2
  134. package/dist/esm/components/ui/autocomplete/index.d.ts +24 -0
  135. package/dist/esm/components/ui/autocomplete/index.d.ts.map +1 -1
  136. package/dist/esm/components/ui/autocomplete/index.js +24 -0
  137. package/dist/esm/components/ui/badge/index.d.ts +11 -7
  138. package/dist/esm/components/ui/badge/index.d.ts.map +1 -1
  139. package/dist/esm/components/ui/badge/index.js +20 -8
  140. package/dist/esm/components/ui/button/index.d.ts +3 -3
  141. package/dist/esm/components/ui/copy-field/index.d.ts +25 -1
  142. package/dist/esm/components/ui/copy-field/index.d.ts.map +1 -1
  143. package/dist/esm/components/ui/copy-field/index.js +16 -8
  144. package/dist/esm/components/ui/dialog/index.d.ts +1 -1
  145. package/dist/esm/components/ui/dialog/index.d.ts.map +1 -1
  146. package/dist/esm/components/ui/dialog/index.js +3 -3
  147. package/dist/esm/components/ui/file-upload/index.d.ts +159 -0
  148. package/dist/esm/components/ui/file-upload/index.d.ts.map +1 -1
  149. package/dist/esm/components/ui/file-upload/index.js +307 -5
  150. package/dist/esm/components/ui/icon-button/index.d.ts +1 -1
  151. package/dist/esm/components/ui/otp-input/index.js +1 -1
  152. package/dist/esm/components/ui/radio-group/index.js +1 -1
  153. package/dist/esm/components/ui/select/index.d.ts.map +1 -1
  154. package/dist/esm/components/ui/select/index.js +8 -4
  155. package/dist/esm/components/ui/sheet/index.d.ts +42 -0
  156. package/dist/esm/components/ui/sheet/index.d.ts.map +1 -1
  157. package/dist/esm/components/ui/sheet/index.js +48 -15
  158. package/dist/esm/components/ui/sidebar/index.d.ts +1 -0
  159. package/dist/esm/components/ui/sidebar/index.d.ts.map +1 -1
  160. package/dist/esm/components/ui/sidebar/index.js +1 -0
  161. package/dist/esm/components/ui/sidebar/sidebar-expand-button.d.ts +1 -1
  162. package/dist/esm/components/ui/sidebar/sidebar-expand-button.d.ts.map +1 -1
  163. package/dist/esm/components/ui/sidebar/sidebar-expand-button.js +15 -1
  164. package/dist/esm/components/ui/sidebar/sidebar-item.d.ts +1 -1
  165. package/dist/esm/components/ui/sidebar/sidebar-item.d.ts.map +1 -1
  166. package/dist/esm/components/ui/sidebar/sidebar-item.js +28 -4
  167. package/dist/esm/components/ui/sidebar/sidebar-provider.d.ts +58 -0
  168. package/dist/esm/components/ui/sidebar/sidebar-provider.d.ts.map +1 -1
  169. package/dist/esm/components/ui/sidebar/sidebar-provider.js +143 -1
  170. package/dist/esm/components/ui/sidebar/sidebar-root.d.ts +38 -1
  171. package/dist/esm/components/ui/sidebar/sidebar-root.d.ts.map +1 -1
  172. package/dist/esm/components/ui/sidebar/sidebar-root.js +113 -7
  173. package/dist/esm/components/ui/sidebar/sidebar-trigger.d.ts +45 -0
  174. package/dist/esm/components/ui/sidebar/sidebar-trigger.d.ts.map +1 -0
  175. package/dist/esm/components/ui/sidebar/sidebar-trigger.js +53 -0
  176. package/dist/esm/components/ui/textarea/index.js +1 -1
  177. package/dist/esm/components/ui/toast/toaster.d.ts +5 -1
  178. package/dist/esm/components/ui/toast/toaster.d.ts.map +1 -1
  179. package/dist/esm/components/ui/toast/toaster.js +3 -2
  180. package/dist/esm/enterprise/data-table/index.d.ts +77 -7
  181. package/dist/esm/enterprise/data-table/index.d.ts.map +1 -1
  182. package/dist/esm/enterprise/data-table/index.js +57 -12
  183. package/dist/esm/enterprise/date-range-picker/index.js +4 -4
  184. package/dist/esm/enterprise/number-input/index.d.ts.map +1 -1
  185. package/dist/esm/enterprise/number-input/index.js +1 -1
  186. package/dist/esm/hooks/use-toast.d.ts +18 -2
  187. package/dist/esm/hooks/use-toast.d.ts.map +1 -1
  188. package/dist/esm/hooks/use-toast.js +60 -16
  189. package/dist/esm/index.d.ts +2 -0
  190. package/dist/esm/index.d.ts.map +1 -1
  191. package/dist/esm/index.js +2 -0
  192. package/dist/esm/lib/utils.d.ts +2 -0
  193. package/dist/esm/lib/utils.d.ts.map +1 -1
  194. package/dist/esm/lib/utils.js +10 -0
  195. package/dist/esm/toast/helpers.d.ts +13 -0
  196. package/dist/esm/toast/helpers.d.ts.map +1 -1
  197. package/dist/esm/toast/helpers.js +8 -1
  198. package/dist/globals.css +81 -31
  199. package/dist/hooks/use-toast.d.ts +18 -2
  200. package/dist/hooks/use-toast.d.ts.map +1 -1
  201. package/dist/hooks/use-toast.js +61 -16
  202. package/dist/index.d.ts +2 -0
  203. package/dist/index.d.ts.map +1 -1
  204. package/dist/index.js +2 -0
  205. package/dist/lib/utils.d.ts +2 -0
  206. package/dist/lib/utils.d.ts.map +1 -1
  207. package/dist/lib/utils.js +11 -0
  208. package/dist/toast/helpers.d.ts +13 -0
  209. package/dist/toast/helpers.d.ts.map +1 -1
  210. package/dist/toast/helpers.js +8 -1
  211. package/package.json +7 -4
@@ -6,19 +6,125 @@ const jsx_runtime_1 = require("react/jsx-runtime");
6
6
  const class_variance_authority_1 = require("class-variance-authority");
7
7
  const sidebar_provider_1 = require("./sidebar-provider");
8
8
  const utils_1 = require("../../../lib/utils");
9
+ const sheet_1 = require("../sheet");
10
+ /**
11
+ * ⛔ THE WIDTHS ARE TOKENS, NOT LITERALS.
12
+ *
13
+ * They were `w-[72px]` and `w-[244px]`, which a consumer could only override by
14
+ * winning a specificity argument with `cn`. `--sidebar-width` and
15
+ * `--sidebar-width-collapsed` are declared in `globals.css`, so the default
16
+ * still ships with the stylesheet, and a consumer re-points either one with
17
+ * `className="[--sidebar-width:280px]"` ON THE RAIL, which moves the rail and
18
+ * the mobile drawer together (see `liftWidthOverrides` below), or with a rule
19
+ * on `:root`/`html`.
20
+ *
21
+ * ⚠️ A DECLARATION ON A REACT-TREE ANCESTOR REACHES THE RAIL AND NOT THE
22
+ * DRAWER. Custom properties inherit down the DOM tree, and the drawer is
23
+ * portalled to `document.body`, so a layout wrapper is not an ancestor of it.
24
+ * `:root` is, which is why it is named above and a wrapper is not.
25
+ */
9
26
  const sidebarVariants = (0, class_variance_authority_1.cva)('group/sidebar shadow-sidebar relative flex flex-col transition-[width] duration-300 ease-in-out', {
10
27
  variants: {
11
28
  collapsed: {
12
- true: 'w-[72px]',
13
- false: 'w-[244px]'
29
+ true: 'w-[var(--sidebar-width-collapsed)]',
30
+ false: 'w-[var(--sidebar-width)]'
14
31
  }
15
32
  },
16
33
  defaultVariants: {
17
34
  collapsed: false
18
35
  }
19
36
  });
20
- const SidebarRoot = ({ className, ...props }) => {
21
- const { isCollapsed } = (0, sidebar_provider_1.useSidebar)();
22
- return ((0, jsx_runtime_1.jsx)("nav", { "data-slot": "sidebar-root", className: (0, utils_1.cn)(sidebarVariants({ collapsed: isCollapsed }), className), "data-collapsed": isCollapsed, ...props }));
37
+ /**
38
+ * Tailwind arbitrary-property classes that re-point the sidebar geometry, e.g.
39
+ * `[--sidebar-width:320px]` or `[--sidebar-width-collapsed:88px]`.
40
+ *
41
+ * ⚠️ ANCHORED TO A CLASS BOUNDARY, WHICH IS THE WHOLE POINT OF THE LOOKBEHIND.
42
+ * Unanchored it also matched the tail of `md:[--sidebar-width:320px]` and
43
+ * lifted it with the prefix stripped — an override a consumer scoped to
44
+ * ≥768px, applied unconditionally to the drawer that only exists below it.
45
+ */
46
+ const WIDTH_OVERRIDE = /(?<=^|\s)\[--sidebar-width(?:-collapsed)?:[^\]]+\]/g;
47
+ /**
48
+ * ⛔ THE ONE PART OF THE CONSUMER'S className THAT HAS TO CROSS THE PORTAL.
49
+ *
50
+ * `SheetContent` mounts through `SheetPortal` into `document.body`, and custom
51
+ * properties inherit down the DOM tree rather than the React tree. So the
52
+ * documented override — a class on the rail — set `--sidebar-width` on an
53
+ * element the drawer is not a descendant of, and the whole className that DOES
54
+ * travel lands on the inner `<nav>`, which is BELOW the element that reads the
55
+ * variable. Measured in Chromium: rail 320px, drawer 244px. Following the
56
+ * instruction handed to product-console produced a 280px rail on desktop and a
57
+ * 244px drawer on a phone, silently.
58
+ *
59
+ * ⚠️ ONLY THE GEOMETRY TOKENS. The rest of a rail className is rail-shaped —
60
+ * `h-full`, and the `data-[collapsed=false]:min-w-70` all eight console
61
+ * sidebars pass — and putting that on the sheet would reinstate the
62
+ * 280px-inside-a-244px-sheet overflow from the other side.
63
+ */
64
+ function liftWidthOverrides(className) {
65
+ return className?.match(WIDTH_OVERRIDE)?.join(' ');
66
+ }
67
+ /**
68
+ * ⛔ BELOW 768px A RAIL IS NOT A LAYOUT — once a consumer opts in.
69
+ *
70
+ * The widths above are a fixed share of a flex row, so at 390px the expanded
71
+ * rail took 63% of the screen and left the page 146px, and the only affordance
72
+ * — `SidebarExpandButton` — shrinks it to 72px, which is still 18% and still
73
+ * permanently on screen. There was nothing that removed it.
74
+ *
75
+ * With `mobile="drawer"` the rail is replaced below the breakpoint by an
76
+ * overlay drawer. Escape, the backdrop click, the focus trap and returning
77
+ * focus to whatever opened it are Radix Dialog's, through the kit's own
78
+ * `Sheet`; none of that is re-implemented here.
79
+ */
80
+ const SidebarRoot = ({ className, id, mobile = 'inline', mobileTitle = 'Navigation', ...props }) => {
81
+ const context = (0, sidebar_provider_1.useSidebar)();
82
+ const { isCollapsed, isMobile, openMobile, setOpenMobile, restoreDrawerFocus, sidebarId } = context;
83
+ if (!isMobile || mobile === 'inline') {
84
+ return ((0, jsx_runtime_1.jsx)("nav", { "data-slot": "sidebar-root", className: (0, utils_1.cn)(sidebarVariants({ collapsed: isCollapsed }), className), "data-collapsed": isCollapsed, ...props, id: mobile === 'drawer' ? sidebarId : id }));
85
+ }
86
+ return ((0, jsx_runtime_1.jsx)(sidebar_provider_1.SidebarContext.Provider, { value: { ...context, isCollapsed: false, isDrawer: true }, children: (0, jsx_runtime_1.jsx)(sheet_1.Sheet, { open: openMobile, onOpenChange: setOpenMobile, children: (0, jsx_runtime_1.jsxs)(sheet_1.SheetContent, { side: "left",
87
+ // The Sheet's own paddings are built for a form panel; a rail brings
88
+ // its own. `max-w-full` so a consumer widening `--sidebar-width` past
89
+ // the viewport still cannot push the drawer off screen. The lifted
90
+ // override goes LAST so it re-points the variable this element reads.
91
+ //
92
+ // ⛔ THE `max-sm:` PAIR IS NOT A DUPLICATE. `SheetContent` grew a
93
+ // phone step — `max-sm:w-full max-sm:px-4` — because a form panel at
94
+ // two fifths of a 390px screen is unreadable. A NAVIGATION drawer is
95
+ // the one panel that must not take it: 244px over a dimmed page is
96
+ // the design, and full-bleed removes the tap-outside-to-close target
97
+ // that every other way out of this drawer is an alternative to.
98
+ // tailwind-merge drops a conflicting class only when the modifiers
99
+ // match, so a bare `w-[…]` cannot reach `max-sm:w-full` and these two
100
+ // tokens are what state the exemption. Measured: without them the
101
+ // drawer is 390px wide with 16px of inset on a 390px phone.
102
+ className: (0, utils_1.cn)('w-[var(--sidebar-width)] max-w-full gap-0 p-0 max-sm:w-[var(--sidebar-width)] max-sm:p-0', liftWidthOverrides(className)), "aria-describedby": undefined,
103
+ // ⛔ RADIX'S OWN RESTORE GOES NOWHERE HERE. `DialogContent`
104
+ // preventDefaults the focus scope's restore and focuses the ref a
105
+ // `SheetTrigger` would have filled; this drawer is opened from provider
106
+ // state, so that ref is null and closing dropped focus onto `<body>`.
107
+ // Running first means `composeEventHandlers` skips Radix's half.
108
+ onCloseAutoFocus: (event) => {
109
+ event.preventDefault();
110
+ restoreDrawerFocus();
111
+ }, children: [(0, jsx_runtime_1.jsx)(sheet_1.SheetTitle, { className: "sr-only", children: mobileTitle }), (0, jsx_runtime_1.jsx)("nav", { "data-slot": "sidebar-root", "data-mobile": "true",
112
+ /* ⛔ NO `data-collapsed` HERE. The consumer className is forwarded
113
+ verbatim, and all eight console sidebars pass
114
+ `data-[collapsed=false]:min-w-70`; stamping the attribute armed
115
+ it, giving the navigation a 280px min-width inside a 244px sheet.
116
+ `min-width` resolves after `max-width`, so the sheet could not
117
+ claw it back and the contents were 36px wider than the drawer.
118
+ `data-mobile` already names this surface and a drawer is never
119
+ collapsed, so the attribute had nothing left to say. */
120
+ className: (0, utils_1.cn)(sidebarVariants({ collapsed: false }), 'h-full w-full shadow-none', className), ...props,
121
+ /* ⛔ AFTER THE SPREAD, AND NOT THE CALLER'S. This is the target of
122
+ `SidebarTrigger`'s `aria-controls` and the handle the provider
123
+ uses to put focus back into the navigation when the viewport
124
+ grows. Spread first, a caller that set `id` replaced it and left
125
+ a dangling `aria-controls` — which axe reports as a CRITICAL
126
+ `aria-valid-attr-value` — and a focus restore that lands on
127
+ `<body>`. Both silent. */
128
+ id: sidebarId })] }) }) }));
23
129
  };
24
130
  exports.SidebarRoot = SidebarRoot;
@@ -0,0 +1,45 @@
1
+ import React from 'react';
2
+ import { IconButton } from '../icon-button';
3
+ export type SidebarTriggerProps = React.ComponentProps<typeof IconButton>;
4
+ /**
5
+ * The control that opens the mobile drawer.
6
+ *
7
+ * Hidden from 768px up by default, because above it the rail is already on
8
+ * screen and a second way to reach it is a second thing to explain; a consumer
9
+ * that wants it everywhere passes its own display class.
10
+ *
11
+ * ⛔ THE BREAKPOINT IS IN PIXELS, AND `md:hidden` IS NOT A SPELLING OF IT.
12
+ *
13
+ * Whether the rail became a drawer is decided by `SIDEBAR_MOBILE_QUERY`, which
14
+ * is `not (min-width: 768px)` — pixels. `md:` is 48rem, and rem inside a MEDIA
15
+ * QUERY resolves against the browser's default font size rather than anything
16
+ * the page declares, so the two flip at different widths the moment a reader
17
+ * changes that setting. At Chrome's "Small" (12px) `md:` turns over at 576px
18
+ * while the drawer still turns over at 768px: every window between them showed
19
+ * NO RAIL AND NO HAMBURGER, which is a console with no navigation at all.
20
+ * Measured on product-console; the band is four of this package's own fixtures
21
+ * in `scripts/measure-phone-shapes.mjs`. At "Very large" (20px) the same
22
+ * mismatch runs the other way and 768–959px drew both.
23
+ *
24
+ * `min-[768px]:hidden` is the exact complement of that query, at every font
25
+ * size AND at any sub-pixel precision — which is why the query next door is
26
+ * spelled as a negation rather than as `(max-width: 767px)`: that pair leaves
27
+ * the open interval (767, 768) matched by neither, and a viewport there draws
28
+ * the rail plus a hamburger that opens nothing.
29
+ *
30
+ * ⚠️ THE PROPERTY IS ABOUT THE TWO MEDIA DECISIONS, NOT ABOUT THE FIRST PAINT.
31
+ * The harness enforces one of the two and never neither or both, which holds
32
+ * wherever `isMobile` has been read. `isMobile` starts false on purpose — there
33
+ * is no viewport on the server — and is corrected in an effect, so on a phone
34
+ * the first painted frame shows the inline rail while this control is already
35
+ * on screen. That one hydration is the deliberate price of hydration safety,
36
+ * documented on `SidebarProvider`'s own effect, and predates this class.
37
+ *
38
+ * ⚠️ `aria-controls` IS SET ONLY WHILE THE DRAWER EXISTS. Radix unmounts the
39
+ * dialog content when closed, and a reference to an element that is not in the
40
+ * DOM is what axe-core reports as a CRITICAL `aria-valid-attr-value` — the same
41
+ * defect the Autocomplete panel was repaired for (console-sdk#150). A toggle
42
+ * with `aria-expanded="false"` and no `aria-controls` is complete on its own.
43
+ */
44
+ export declare const SidebarTrigger: ({ className, onClick, "aria-label": ariaLabel, ...props }: SidebarTriggerProps) => React.JSX.Element;
45
+ //# sourceMappingURL=sidebar-trigger.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sidebar-trigger.d.ts","sourceRoot":"","sources":["../../../../src/components/ui/sidebar/sidebar-trigger.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,MAAM,OAAO,CAAA;AAGzB,OAAO,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAA;AAG3C,MAAM,MAAM,mBAAmB,GAAG,KAAK,CAAC,cAAc,CAAC,OAAO,UAAU,CAAC,CAAA;AAEzE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuCG;AACH,eAAO,MAAM,cAAc,GAAI,2DAK5B,mBAAmB,sBAqBrB,CAAA"}
@@ -0,0 +1,57 @@
1
+ 'use client';
2
+ "use strict";
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ exports.SidebarTrigger = void 0;
5
+ const jsx_runtime_1 = require("react/jsx-runtime");
6
+ const lucide_react_1 = require("lucide-react");
7
+ const utils_1 = require("../../../lib/utils");
8
+ const icon_button_1 = require("../icon-button");
9
+ const sidebar_provider_1 = require("./sidebar-provider");
10
+ /**
11
+ * The control that opens the mobile drawer.
12
+ *
13
+ * Hidden from 768px up by default, because above it the rail is already on
14
+ * screen and a second way to reach it is a second thing to explain; a consumer
15
+ * that wants it everywhere passes its own display class.
16
+ *
17
+ * ⛔ THE BREAKPOINT IS IN PIXELS, AND `md:hidden` IS NOT A SPELLING OF IT.
18
+ *
19
+ * Whether the rail became a drawer is decided by `SIDEBAR_MOBILE_QUERY`, which
20
+ * is `not (min-width: 768px)` — pixels. `md:` is 48rem, and rem inside a MEDIA
21
+ * QUERY resolves against the browser's default font size rather than anything
22
+ * the page declares, so the two flip at different widths the moment a reader
23
+ * changes that setting. At Chrome's "Small" (12px) `md:` turns over at 576px
24
+ * while the drawer still turns over at 768px: every window between them showed
25
+ * NO RAIL AND NO HAMBURGER, which is a console with no navigation at all.
26
+ * Measured on product-console; the band is four of this package's own fixtures
27
+ * in `scripts/measure-phone-shapes.mjs`. At "Very large" (20px) the same
28
+ * mismatch runs the other way and 768–959px drew both.
29
+ *
30
+ * `min-[768px]:hidden` is the exact complement of that query, at every font
31
+ * size AND at any sub-pixel precision — which is why the query next door is
32
+ * spelled as a negation rather than as `(max-width: 767px)`: that pair leaves
33
+ * the open interval (767, 768) matched by neither, and a viewport there draws
34
+ * the rail plus a hamburger that opens nothing.
35
+ *
36
+ * ⚠️ THE PROPERTY IS ABOUT THE TWO MEDIA DECISIONS, NOT ABOUT THE FIRST PAINT.
37
+ * The harness enforces one of the two and never neither or both, which holds
38
+ * wherever `isMobile` has been read. `isMobile` starts false on purpose — there
39
+ * is no viewport on the server — and is corrected in an effect, so on a phone
40
+ * the first painted frame shows the inline rail while this control is already
41
+ * on screen. That one hydration is the deliberate price of hydration safety,
42
+ * documented on `SidebarProvider`'s own effect, and predates this class.
43
+ *
44
+ * ⚠️ `aria-controls` IS SET ONLY WHILE THE DRAWER EXISTS. Radix unmounts the
45
+ * dialog content when closed, and a reference to an element that is not in the
46
+ * DOM is what axe-core reports as a CRITICAL `aria-valid-attr-value` — the same
47
+ * defect the Autocomplete panel was repaired for (console-sdk#150). A toggle
48
+ * with `aria-expanded="false"` and no `aria-controls` is complete on its own.
49
+ */
50
+ const SidebarTrigger = ({ className, onClick, 'aria-label': ariaLabel = 'Open navigation', ...props }) => {
51
+ const { openMobile, setOpenMobile, sidebarId } = (0, sidebar_provider_1.useSidebar)();
52
+ return ((0, jsx_runtime_1.jsx)(icon_button_1.IconButton, { type: "button", variant: "outline", "data-slot": "sidebar-trigger", "aria-label": ariaLabel, "aria-expanded": openMobile, "aria-controls": openMobile ? sidebarId : undefined, className: (0, utils_1.cn)('min-[768px]:hidden', className), onClick: (event) => {
53
+ onClick?.(event);
54
+ setOpenMobile(!openMobile);
55
+ }, ...props, children: (0, jsx_runtime_1.jsx)(lucide_react_1.Menu, { "aria-hidden": true }) }));
56
+ };
57
+ exports.SidebarTrigger = SidebarTrigger;
@@ -4,5 +4,5 @@ exports.Textarea = Textarea;
4
4
  const jsx_runtime_1 = require("react/jsx-runtime");
5
5
  const utils_1 = require("../../../lib/utils");
6
6
  function Textarea({ className, ...props }) {
7
- return ((0, jsx_runtime_1.jsx)("textarea", { className: (0, utils_1.cn)('border-input bg-background ring-offset-background placeholder:text-muted-foreground focus-visible:ring-ring flex w-full rounded-md border px-3 py-2 text-base focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:outline-hidden', 'read-only:bg-muted read-only:cursor-default read-only:caret-transparent read-only:opacity-50 read-only:select-text read-only:focus:ring-0 read-only:focus:ring-offset-0 read-only:focus:outline-hidden', 'disabled:cursor-not-allowed disabled:opacity-50', className), ...props }));
7
+ return ((0, jsx_runtime_1.jsx)("textarea", { className: (0, utils_1.cn)('border-input-border bg-background ring-offset-background placeholder:text-muted-foreground focus-visible:ring-ring flex w-full rounded-md border px-3 py-2 text-base focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:outline-hidden', 'read-only:bg-muted read-only:cursor-default read-only:caret-transparent read-only:opacity-50 read-only:select-text read-only:focus:ring-0 read-only:focus:ring-offset-0 read-only:focus:outline-hidden', 'disabled:cursor-not-allowed disabled:opacity-50', className), ...props }));
8
8
  }
@@ -1,3 +1,5 @@
1
+ import type { ComponentProps } from 'react';
2
+ import { Toaster as SonnerToaster } from 'sonner';
1
3
  type ToasterProps = {
2
4
  /**
3
5
  * Forces the toast theme. Omit it inside a `ThemeProvider` to follow the
@@ -5,7 +7,9 @@ type ToasterProps = {
5
7
  * leaving the light/dark call to sonner's own media query.
6
8
  */
7
9
  theme?: 'light' | 'dark' | 'system';
10
+ /** Where the toast stack sits on screen. Defaults to `'bottom-right'`. */
11
+ position?: ComponentProps<typeof SonnerToaster>['position'];
8
12
  };
9
- export declare function Toaster({ theme }: ToasterProps): import("react").JSX.Element;
13
+ export declare function Toaster({ theme, position }: ToasterProps): import("react").JSX.Element;
10
14
  export {};
11
15
  //# sourceMappingURL=toaster.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"toaster.d.ts","sourceRoot":"","sources":["../../../../src/components/ui/toast/toaster.tsx"],"names":[],"mappings":"AAMA,KAAK,YAAY,GAAG;IAClB;;;;OAIG;IACH,KAAK,CAAC,EAAE,OAAO,GAAG,MAAM,GAAG,QAAQ,CAAA;CACpC,CAAA;AAED,wBAAgB,OAAO,CAAC,EAAE,KAAK,EAAE,EAAE,YAAY,+BAqB9C"}
1
+ {"version":3,"file":"toaster.d.ts","sourceRoot":"","sources":["../../../../src/components/ui/toast/toaster.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,OAAO,CAAA;AAC3C,OAAO,EAAE,OAAO,IAAI,aAAa,EAAE,MAAM,QAAQ,CAAA;AAKjD,KAAK,YAAY,GAAG;IAClB;;;;OAIG;IACH,KAAK,CAAC,EAAE,OAAO,GAAG,MAAM,GAAG,QAAQ,CAAA;IACnC,0EAA0E;IAC1E,QAAQ,CAAC,EAAE,cAAc,CAAC,OAAO,aAAa,CAAC,CAAC,UAAU,CAAC,CAAA;CAC5D,CAAA;AAED,wBAAgB,OAAO,CAAC,EAAE,KAAK,EAAE,QAAyB,EAAE,EAAE,YAAY,+BAqBzE"}
@@ -4,12 +4,13 @@ Object.defineProperty(exports, "__esModule", { value: true });
4
4
  exports.Toaster = Toaster;
5
5
  const jsx_runtime_1 = require("react/jsx-runtime");
6
6
  const sonner_1 = require("sonner");
7
+ const use_toast_1 = require("../../../hooks/use-toast");
7
8
  const theme_provider_1 = require("../../../theme/theme-provider");
8
- function Toaster({ theme }) {
9
+ function Toaster({ theme, position = 'bottom-right' }) {
9
10
  // Optional read: a Toaster mounted outside any ThemeProvider must still
10
11
  // render, so this cannot go through `useTheme`, which throws.
11
12
  const themeContext = (0, theme_provider_1.useOptionalTheme)();
12
- return ((0, jsx_runtime_1.jsx)(sonner_1.Toaster, { theme: theme ?? themeContext?.resolvedTheme ?? 'system', position: "bottom-right", visibleToasts: 3, duration: 10000, expand: false, closeButton: true, richColors: false, offset: 16, gap: 8, toastOptions: {
13
+ return ((0, jsx_runtime_1.jsx)(sonner_1.Toaster, { theme: theme ?? themeContext?.resolvedTheme ?? 'system', position: position, visibleToasts: 3, duration: use_toast_1.DEFAULT_DURATION, expand: false, closeButton: true, richColors: false, offset: 16, gap: 8, toastOptions: {
13
14
  className: 'font-sans !text-sm !font-medium'
14
15
  } }));
15
16
  }
@@ -1,13 +1,53 @@
1
- import { type ColumnDef, type OnChangeFn, type RowData, type RowSelectionState, type SortingState } from '@tanstack/react-table';
1
+ import { type ColumnDef, type OnChangeFn, type RowData, type RowSelectionState, type SortingState, type VisibilityState } from '@tanstack/react-table';
2
2
  import { type EmptyStateProps } from '../empty-state';
3
3
  /**
4
- * Per-column opt-in for ledger numeric alignment. Set `meta: { numeric: true }`
5
- * on a money/number `ColumnDef` and DataTable right-aligns both the header and
6
- * its body cells (mono tabular figures).
4
+ * Per-column channel into the cells DataTable renders on the consumer's behalf:
5
+ * how a column aligns, what classes it carries on head and body, and whether it
6
+ * renders its own `<td>` at all. The member names are frozen by plan
7
+ * 2026-09-21-console-simplification C9.
7
8
  */
8
9
  declare module '@tanstack/react-table' {
9
10
  interface ColumnMeta<TData extends RowData, TValue> {
11
+ /**
12
+ * Ledger numeric treatment: right-aligns the header and its body cells and
13
+ * gives the body mono tabular figures. `align` overrides the alignment half
14
+ * on its own — the figures stay mono.
15
+ */
10
16
  numeric?: boolean;
17
+ /**
18
+ * Column alignment, header and body. Beats `numeric`'s implicit right, so a
19
+ * money column can read left or centre without losing its figure treatment.
20
+ * With `renderOwnCell` it reaches the header only — the body cell is the
21
+ * consumer's, and so is aligning it.
22
+ */
23
+ align?: 'left' | 'right' | 'center';
24
+ /**
25
+ * Extra classes on this column's `<th>`, merged AFTER the table-level
26
+ * `headClassName` so one column can override what the table set for all.
27
+ */
28
+ headerClassName?: string;
29
+ /**
30
+ * Extra classes on this column's body `<td>`, merged after the numeric and
31
+ * density treatments. IGNORED when `renderOwnCell` is set: there is no cell
32
+ * of the table's left to carry them. Use `headerClassName` for the `<th>`,
33
+ * which the table still owns either way.
34
+ */
35
+ className?: string;
36
+ /**
37
+ * The column's `cell` renderer emits its own `<td>`; DataTable renders it
38
+ * bare rather than wrapping it. The HEADER is still the table's `<th>` —
39
+ * nothing about owning a body cell makes a column own its header, and a
40
+ * table whose head and body disagree on cell count is broken markup.
41
+ *
42
+ * So the split is by element, not by column: the head still takes `align`,
43
+ * `numeric`, `headerClassName` and the declared width, because the `<th>`
44
+ * is the table's to style; the BODY takes none of them, because the `<td>`
45
+ * is the consumer's. A money column declaring `align: 'right'` therefore
46
+ * gets a right-aligned header and must right-align its own cell to match —
47
+ * leave the cell's alignment out and the figures sit under a heading that
48
+ * points somewhere else.
49
+ */
50
+ renderOwnCell?: boolean;
11
51
  }
12
52
  }
13
53
  /**
@@ -71,6 +111,35 @@ type SortingProps = {
71
111
  /** Controlled sort updater (TanStack `OnChangeFn`). */
72
112
  onSortingChange: OnChangeFn<SortingState>;
73
113
  };
114
+ /**
115
+ * Column visibility is controlled but, deliberately, NOT a required pair — a
116
+ * departure from the two unions above, and not drift. Those two are unions
117
+ * because each has an `enable*` flag whose "on" state is meaningless without
118
+ * the controlled pair. TanStack has no enable flag for visibility: the model is
119
+ * always running, and the only shipped consumer passes STATE WITH NO UPDATER,
120
+ * because a separate dropdown owns the state on the page. Requiring the pair
121
+ * would refuse that usage.
122
+ *
123
+ * What the union does forbid is the updater ALONE: TanStack reads an
124
+ * `on[State]Change` callback as a declaration of controlled state, so a
125
+ * callback with no `columnVisibility` freezes visibility at its initial value
126
+ * while the callback keeps firing. Prop names frozen by plan
127
+ * 2026-09-21-console-simplification C9.
128
+ *
129
+ * Omit both and the table keeps its own visibility state, exactly as before.
130
+ */
131
+ type ColumnVisibilityProps = {
132
+ /** Controlled column-visibility state (TanStack `VisibilityState`). */
133
+ columnVisibility: VisibilityState;
134
+ /**
135
+ * Controlled updater (TanStack `OnChangeFn`). Optional: a consumer may
136
+ * drive the state from outside the table entirely.
137
+ */
138
+ onColumnVisibilityChange?: OnChangeFn<VisibilityState>;
139
+ } | {
140
+ columnVisibility?: undefined;
141
+ onColumnVisibilityChange?: undefined;
142
+ };
74
143
  type DataTableBaseProps<TData> = {
75
144
  columns: ColumnDef<TData, unknown>[];
76
145
  data: TData[];
@@ -117,7 +186,8 @@ type DataTableBaseProps<TData> = {
117
186
  /**
118
187
  * Extra classes merged onto every header cell, after the kit label voice —
119
188
  * so a caller can override the voice's size/tracking/color for one table
120
- * without restating the cluster. Per-column alignment stays `meta.numeric`.
189
+ * without restating the cluster. Per column, `meta.headerClassName` merges
190
+ * after this one and `meta.align` / `meta.numeric` own the alignment.
121
191
  */
122
192
  headClassName?: string;
123
193
  /**
@@ -127,7 +197,7 @@ type DataTableBaseProps<TData> = {
127
197
  */
128
198
  footer?: React.ReactNode;
129
199
  };
130
- export type DataTableProps<TData> = DataTableBaseProps<TData> & RowSelectionProps<TData> & SortingProps;
131
- export declare function DataTable<TData>({ columns, data, loading, skeletonRows, loadingLabel, empty, getRowId, flush, density, enableRowSelection, rowSelection, onRowSelectionChange, getRowSelectionLabel, enableSorting, sorting, onSortingChange, onRowActivate, rowHref, className, headClassName, footer }: DataTableProps<TData>): import("react").JSX.Element;
200
+ export type DataTableProps<TData> = DataTableBaseProps<TData> & RowSelectionProps<TData> & SortingProps & ColumnVisibilityProps;
201
+ export declare function DataTable<TData>({ columns, data, loading, skeletonRows, loadingLabel, empty, getRowId, flush, density, enableRowSelection, rowSelection, onRowSelectionChange, getRowSelectionLabel, enableSorting, sorting, onSortingChange, columnVisibility, onColumnVisibilityChange, onRowActivate, rowHref, className, headClassName, footer }: DataTableProps<TData>): import("react").JSX.Element;
132
202
  export {};
133
203
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/enterprise/data-table/index.tsx"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/enterprise/data-table/index.tsx"],"names":[],"mappings":"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"}
@@ -65,8 +65,21 @@ const empty_state_1 = require("../empty-state");
65
65
  const column_size_1 = require("./column-size");
66
66
  const SELECTION_COLUMN_ID = '__select__';
67
67
  const CHECKBOX_CLASS = 'accent-primary block size-4';
68
- /** Right-aligned mono treatment for `meta: { numeric: true }` columns. */
69
- const NUMERIC_CELL_CLASS = 'text-right font-mono tabular-nums';
68
+ /** Figure treatment for `meta: { numeric: true }` columns; the alignment half
69
+ * lives in ALIGN_CELL_CLASS so `meta.align` can override one without the other. */
70
+ const NUMERIC_FIGURE_CLASS = 'font-mono tabular-nums';
71
+ /**
72
+ * Body-cell alignment, one class per direction rather than a single string with
73
+ * `text-right` baked in: an explicit `meta.align` has to beat `numeric` by
74
+ * choosing the class, not by out-ordering it — tailwind-merge resolving two
75
+ * conflicting alignment utilities by source order is not a contract worth
76
+ * relying on.
77
+ */
78
+ const ALIGN_CELL_CLASS = {
79
+ left: 'text-left',
80
+ center: 'text-center',
81
+ right: 'text-right'
82
+ };
70
83
  /**
71
84
  * Keydown events whose target sits inside one of these are never hijacked
72
85
  * by the row-level keyboard handler — Space in a checkbox and Enter on an
@@ -116,7 +129,7 @@ function createSelectionColumn(getLabel) {
116
129
  cell: ({ row }) => (0, jsx_runtime_1.jsx)(RowSelectionCheckbox, { row: row, getLabel: getLabel })
117
130
  };
118
131
  }
119
- function DataTable({ columns, data, loading = false, skeletonRows = 8, loadingLabel = 'Loading table rows', empty, getRowId, flush = false, density = 'comfortable', enableRowSelection = false, rowSelection, onRowSelectionChange, getRowSelectionLabel, enableSorting = false, sorting, onSortingChange, onRowActivate, rowHref, className, headClassName, footer }) {
132
+ function DataTable({ columns, data, loading = false, skeletonRows = 8, loadingLabel = 'Loading table rows', empty, getRowId, flush = false, density = 'comfortable', enableRowSelection = false, rowSelection, onRowSelectionChange, getRowSelectionLabel, enableSorting = false, sorting, onSortingChange, columnVisibility, onColumnVisibilityChange, onRowActivate, rowHref, className, headClassName, footer }) {
120
133
  const tableColumns = (0, react_1.useMemo)(() => enableRowSelection
121
134
  ? [createSelectionColumn(getRowSelectionLabel), ...columns]
122
135
  : columns, [columns, enableRowSelection, getRowSelectionLabel]);
@@ -142,12 +155,33 @@ function DataTable({ columns, data, loading = false, skeletonRows = 8, loadingLa
142
155
  manualSorting: true,
143
156
  enableSorting,
144
157
  onSortingChange: enableSorting ? onSortingChange : undefined,
158
+ // ⛔ EACH VISIBILITY ENTRY IS GUARDED ON ITS OWN PROP, NEVER ON THE OTHER.
159
+ //
160
+ // TanStack merges options as `{...defaultOptions, ...options}` and state as
161
+ // `{...internalState, ...options.state}`, both plain spreads — so a key
162
+ // present and holding `undefined` OVERWRITES what it was meant to leave
163
+ // alone. `onColumnVisibilityChange: undefined` replaces the feature's own
164
+ // `makeStateUpdater`, and `setColumnVisibility` reads
165
+ // `options.onColumnVisibilityChange == null ? void 0 : ...`, so it (and
166
+ // every `column.toggleVisibility` behind it) becomes a silent no-op.
167
+ // `columnVisibility: undefined` replaces the table's initial `{}`, so a
168
+ // consumer reading the slice gets `undefined` instead of an empty object.
169
+ //
170
+ // Guarding the UPDATER on the STATE put that `undefined` back for the
171
+ // union's first arm — state with no updater, which is the shipped console
172
+ // usage, where a separate dropdown owns the state. A table that passes
173
+ // neither prop is uncontrolled, exactly as before.
174
+ ...(onColumnVisibilityChange !== undefined && { onColumnVisibilityChange }),
145
175
  state: {
146
176
  rowSelection: enableRowSelection ? (rowSelection ?? {}) : undefined,
147
- sorting: enableSorting ? (sorting ?? []) : undefined
177
+ sorting: enableSorting ? (sorting ?? []) : undefined,
178
+ ...(columnVisibility !== undefined && { columnVisibility })
148
179
  }
149
180
  });
150
- const colCount = tableColumns.length;
181
+ // Visible, not declared: the skeleton grid and the empty row's colSpan have
182
+ // to match the header the table actually rendered, and a hidden column is in
183
+ // `tableColumns` but in no `<th>`.
184
+ const colCount = table.getVisibleLeafColumns().length;
151
185
  const dataRows = table.getRowModel().rows;
152
186
  const rowCount = dataRows.length;
153
187
  // Compact density overrides only THIS table's cell padding (tailwind-merge
@@ -265,13 +299,13 @@ function DataTable({ columns, data, loading = false, skeletonRows = 8, loadingLa
265
299
  : rowCount === 0
266
300
  ? 'No rows'
267
301
  : `${rowCount} rows` }), (0, jsx_runtime_1.jsxs)(table_1.Table, { "aria-busy": loading ? 'true' : undefined, children: [(0, jsx_runtime_1.jsx)(table_1.TableHeader, { children: table.getHeaderGroups().map((headerGroup) => ((0, jsx_runtime_1.jsx)(table_1.TableRow, { className: "hover:bg-transparent", children: headerGroup.headers.map((header) => {
268
- const numeric = header.column.columnDef.meta?.numeric;
269
- return ((0, jsx_runtime_1.jsx)(table_1.TableHead, { align: numeric ? 'right' : undefined, "aria-sort": header.isPlaceholder
302
+ const meta = header.column.columnDef.meta;
303
+ return ((0, jsx_runtime_1.jsx)(table_1.TableHead, { align: meta?.align ?? (meta?.numeric ? 'right' : undefined), "aria-sort": header.isPlaceholder
270
304
  ? undefined
271
305
  : ariaSortFor(header.column),
272
306
  // Only a column that DECLARED sizing gets an inline width;
273
307
  // everything else keeps the auto table layout it has today.
274
- style: (0, column_size_1.readPreferredColumnSize)(header.column), className: (0, utils_1.cn)(typography_1.LABEL_VOICE_CLASS, headDensityClass, header.column.id === SELECTION_COLUMN_ID && 'w-10', headClassName), children: header.isPlaceholder
308
+ style: (0, column_size_1.readPreferredColumnSize)(header.column), className: (0, utils_1.cn)(typography_1.LABEL_VOICE_CLASS, headDensityClass, header.column.id === SELECTION_COLUMN_ID && 'w-10', headClassName, meta?.headerClassName), children: header.isPlaceholder
275
309
  ? null
276
310
  : (0, react_table_1.flexRender)(header.column.columnDef.header, header.getContext()) }, header.id));
277
311
  }) }, headerGroup.id))) }), (0, jsx_runtime_1.jsx)(table_1.TableBody, { children: loading ? (Array.from({ length: skeletonRows }).map((_, rowIndex) => ((0, jsx_runtime_1.jsx)(table_1.TableRow, { className: "hover:bg-transparent", children: Array.from({ length: colCount }).map((__, cellIndex) => ((0, jsx_runtime_1.jsx)(table_1.TableCell, { className: cellDensityClass, children: (0, jsx_runtime_1.jsx)(skeleton_1.Skeleton, { className: "h-4 w-full max-w-[160px]" }) }, `skeleton-${rowIndex}-${cellIndex}`))) }, `skeleton-${rowIndex}`)))) : rowCount === 0 ? ((0, jsx_runtime_1.jsx)(table_1.TableRow, { className: "hover:bg-transparent", children: (0, jsx_runtime_1.jsx)(table_1.TableCell, { colSpan: colCount, className: "p-0", children: (0, jsx_runtime_1.jsx)(empty_state_1.EmptyState, { title: empty?.title ?? 'Nothing here yet', description: empty?.description, icon: empty?.icon, action: empty?.action,
@@ -294,7 +328,18 @@ function DataTable({ columns, data, loading = false, skeletonRows = 8, loadingLa
294
328
  ? (event) => handleRowKeyDown(event, row, rowIndex)
295
329
  : undefined, className: keyboardNav ? ROW_FOCUS_CLASS : undefined, "data-state": enableRowSelection && row.getIsSelected()
296
330
  ? 'selected'
297
- : undefined, children: row.getVisibleCells().map((cell) => ((0, jsx_runtime_1.jsx)(table_1.TableCell, { style: (0, column_size_1.readPreferredColumnSize)(cell.column), className: (0, utils_1.cn)(cell.column.columnDef.meta?.numeric && NUMERIC_CELL_CLASS, cellDensityClass, cell.column.id === SELECTION_COLUMN_ID && 'w-10'), onClick: cell.column.id === SELECTION_COLUMN_ID
298
- ? stopPropagation
299
- : undefined, children: (0, react_table_1.flexRender)(cell.column.columnDef.cell, cell.getContext()) }, cell.id))) }, row.id)))) }), footer ? (0, jsx_runtime_1.jsx)(table_1.TableFooter, { children: footer }) : null] })] }));
331
+ : undefined, children: row.getVisibleCells().map((cell) => {
332
+ const meta = cell.column.columnDef.meta;
333
+ const content = (0, react_table_1.flexRender)(cell.column.columnDef.cell, cell.getContext());
334
+ // The column owns its `<td>`: render the consumer's cell bare.
335
+ // The kit's selection column is built here and carries no
336
+ // meta, so it can never take this path.
337
+ if (meta?.renderOwnCell) {
338
+ return (0, jsx_runtime_1.jsx)(react_1.Fragment, { children: content }, cell.id);
339
+ }
340
+ const align = meta?.align ?? (meta?.numeric ? 'right' : undefined);
341
+ return ((0, jsx_runtime_1.jsx)(table_1.TableCell, { style: (0, column_size_1.readPreferredColumnSize)(cell.column), className: (0, utils_1.cn)(meta?.numeric && NUMERIC_FIGURE_CLASS, align && ALIGN_CELL_CLASS[align], cellDensityClass, cell.column.id === SELECTION_COLUMN_ID && 'w-10', meta?.className), onClick: cell.column.id === SELECTION_COLUMN_ID
342
+ ? stopPropagation
343
+ : undefined, children: content }, cell.id));
344
+ }) }, row.id)))) }), footer ? (0, jsx_runtime_1.jsx)(table_1.TableFooter, { children: footer }) : null] })] }));
300
345
  }
@@ -92,12 +92,12 @@ function DateRangePicker({ value, onValueChange, fromId, toId, fromLabel, toLabe
92
92
  });
93
93
  };
94
94
  const triggerClass = (0, utils_1.cn)(
95
- // border-shadcn-400, not border-input: `--input` is a SURFACE token and is
96
- // white in the light theme, so the trigger had no visible edge next to the
97
- // Input and Select it sits with. font-sans, not font-mono: the dates are
95
+ // border-input-border, not border-input: `--input` is a SURFACE token and
96
+ // is white in the light theme, so the trigger had no visible edge next to
97
+ // the Input and Select it sits with. font-sans, not font-mono: the dates are
98
98
  // figures in the body face with tabular figures, like every other figure in
99
99
  // the console.
100
- 'border-shadcn-400 bg-card hover:bg-secondary focus-visible:ring-ring flex h-9 items-center gap-2 rounded-md border px-3 text-left font-sans text-sm tabular-nums transition-colors duration-150 ease-out focus-visible:ring-2 focus-visible:ring-offset-1 focus-visible:outline-none sm:w-40', invalid && 'border-destructive',
100
+ 'border-input-border bg-card hover:bg-secondary focus-visible:ring-ring flex h-9 items-center gap-2 rounded-md border px-3 text-left font-sans text-sm tabular-nums transition-colors duration-150 ease-out focus-visible:ring-2 focus-visible:ring-offset-1 focus-visible:outline-none sm:w-40', invalid && 'border-destructive',
101
101
  // Conditional rather than `disabled:` variants, matching how this file
102
102
  // already applies `border-destructive`: with the prop absent the class
103
103
  // string stays byte-identical to what consumers render today.
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/enterprise/number-input/index.tsx"],"names":[],"mappings":"AAEA;;;;;;;;;;GAUG;AACH,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAQ9B,MAAM,MAAM,gBAAgB,GAAG;IAC7B,wDAAwD;IACxD,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;IACpB,+EAA+E;IAC/E,aAAa,EAAE,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,KAAK,IAAI,CAAA;IAC5C,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,wCAAwC;IACxC,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,sEAAsE;IACtE,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,yEAAyE;IACzE,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,kEAAkE;IAClE,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,8CAA8C;IAC9C,kBAAkB,CAAC,EAAE,MAAM,CAAA;IAC3B,sDAAsD;IACtD,EAAE,CAAC,EAAE,MAAM,CAAA;IACX,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,WAAW,CAAC,EAAE,MAAM,CAAA;CACrB,CAAA;AAmED;;;;;;;GAOG;AACH,wBAAgB,QAAQ,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAgBpE;AAED;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,CAMxE;AAED,wBAAgB,WAAW,CAAC,EAC1B,KAAK,EACL,aAAa,EACb,GAAG,EACH,GAAG,EACH,IAAQ,EACR,SAAS,EACT,MAAM,EACN,QAAgB,EAChB,EAAE,EACF,SAAS,EACT,WAAW,EACX,YAAY,EAAE,SAAS,EACvB,kBAAkB,EAAE,eAAe,EACpC,EAAE,gBAAgB,qBAgJlB"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/enterprise/number-input/index.tsx"],"names":[],"mappings":"AAEA;;;;;;;;;;GAUG;AACH,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAQ9B,MAAM,MAAM,gBAAgB,GAAG;IAC7B,wDAAwD;IACxD,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;IACpB,+EAA+E;IAC/E,aAAa,EAAE,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,KAAK,IAAI,CAAA;IAC5C,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,wCAAwC;IACxC,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,sEAAsE;IACtE,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,yEAAyE;IACzE,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,kEAAkE;IAClE,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,8CAA8C;IAC9C,kBAAkB,CAAC,EAAE,MAAM,CAAA;IAC3B,sDAAsD;IACtD,EAAE,CAAC,EAAE,MAAM,CAAA;IACX,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,WAAW,CAAC,EAAE,MAAM,CAAA;CACrB,CAAA;AAmED;;;;;;;GAOG;AACH,wBAAgB,QAAQ,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAgBpE;AAED;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,CAMxE;AAED,wBAAgB,WAAW,CAAC,EAC1B,KAAK,EACL,aAAa,EACb,GAAG,EACH,GAAG,EACH,IAAQ,EACR,SAAS,EACT,MAAM,EACN,QAAgB,EAChB,EAAE,EACF,SAAS,EACT,WAAW,EACX,YAAY,EAAE,SAAS,EACvB,kBAAkB,EAAE,eAAe,EACpC,EAAE,gBAAgB,qBA2JlB"}
@@ -248,5 +248,5 @@ function NumberInput({ value, onValueChange, min, max, step = 1, precision, loca
248
248
  };
249
249
  const atMin = typeof min === 'number' && value !== null && value <= min;
250
250
  const atMax = typeof max === 'number' && value !== null && value >= max;
251
- return ((0, jsx_runtime_1.jsxs)("div", { className: (0, utils_1.cn)('inline-flex items-stretch gap-1', className), children: [(0, jsx_runtime_1.jsx)(icon_button_1.IconButton, { type: "button", variant: "outline", disabled: disabled || atMin, "aria-label": `Decrease${ariaLabel ? ` ${ariaLabel}` : ''}`, onClick: () => stepBy(-1), tabIndex: -1, children: (0, jsx_runtime_1.jsx)(lucide_react_1.Minus, { className: "size-4", "aria-hidden": true }) }), (0, jsx_runtime_1.jsx)("div", { className: "w-28", children: (0, jsx_runtime_1.jsx)(input_1.Input, { id: id, inputMode: "decimal", role: "spinbutton", "aria-valuenow": value ?? undefined, "aria-valuemin": min, "aria-valuemax": max, className: "text-right tabular-nums", value: displayValue, placeholder: placeholder, disabled: disabled, "aria-label": ariaLabel, "aria-describedby": ariaDescribedBy, onChange: onChange, onKeyDown: onKeyDown, onFocus: onFocus, onBlur: onBlur }) }), (0, jsx_runtime_1.jsx)(icon_button_1.IconButton, { type: "button", variant: "outline", disabled: disabled || atMax, "aria-label": `Increase${ariaLabel ? ` ${ariaLabel}` : ''}`, onClick: () => stepBy(1), tabIndex: -1, children: (0, jsx_runtime_1.jsx)(lucide_react_1.Plus, { className: "size-4", "aria-hidden": true }) })] }));
251
+ return ((0, jsx_runtime_1.jsxs)("div", { className: (0, utils_1.cn)('inline-flex items-stretch gap-1', className), children: [(0, jsx_runtime_1.jsx)(icon_button_1.IconButton, { className: "shrink-0", type: "button", variant: "outline", disabled: disabled || atMin, "aria-label": `Decrease${ariaLabel ? ` ${ariaLabel}` : ''}`, onClick: () => stepBy(-1), tabIndex: -1, children: (0, jsx_runtime_1.jsx)(lucide_react_1.Minus, { className: "size-4", "aria-hidden": true }) }), (0, jsx_runtime_1.jsx)("div", { className: "w-28 min-w-0", children: (0, jsx_runtime_1.jsx)(input_1.Input, { id: id, inputMode: "decimal", role: "spinbutton", "aria-valuenow": value ?? undefined, "aria-valuemin": min, "aria-valuemax": max, className: "text-right tabular-nums", value: displayValue, placeholder: placeholder, disabled: disabled, "aria-label": ariaLabel, "aria-describedby": ariaDescribedBy, onChange: onChange, onKeyDown: onKeyDown, onFocus: onFocus, onBlur: onBlur }) }), (0, jsx_runtime_1.jsx)(icon_button_1.IconButton, { className: "shrink-0", type: "button", variant: "outline", disabled: disabled || atMax, "aria-label": `Increase${ariaLabel ? ` ${ariaLabel}` : ''}`, onClick: () => stepBy(1), tabIndex: -1, children: (0, jsx_runtime_1.jsx)(lucide_react_1.Plus, { className: "size-4", "aria-hidden": true }) })] }));
252
252
  }
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/components/breadcrumb/index.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AAUzB,MAAM,MAAM,cAAc,GAAG;IAC3B,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,CAAC,EAAE,MAAM,CAAA;CACd,CAAA;AAED,KAAK,eAAe,GAAG;IACrB,KAAK,EAAE,cAAc,EAAE,CAAA;CACxB,CAAA;AAED,eAAO,MAAM,UAAU,GAAI,WAAW,eAAe,sBAoBpD,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/components/breadcrumb/index.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AAUzB,MAAM,MAAM,cAAc,GAAG;IAC3B,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,CAAC,EAAE,MAAM,CAAA;CACd,CAAA;AAED,KAAK,eAAe,GAAG;IACrB,KAAK,EAAE,cAAc,EAAE,CAAA;CACxB,CAAA;AAED,eAAO,MAAM,UAAU,GAAI,WAAW,eAAe,sBAmDpD,CAAA"}
@@ -2,5 +2,29 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import React from 'react';
3
3
  import { Breadcrumb as BaseBreadcrumb, BreadcrumbItem, BreadcrumbLink, BreadcrumbList, BreadcrumbPage, BreadcrumbSeparator } from '../../components/ui/breadcrumb/index.js';
4
4
  export const Breadcrumb = ({ paths }) => {
5
- return (_jsx(BaseBreadcrumb, { children: _jsx(BreadcrumbList, { children: paths.map((path, index) => (_jsxs(React.Fragment, { children: [_jsx(BreadcrumbItem, { children: path.href ? (_jsx(BreadcrumbLink, { href: path.href, children: path.name })) : (_jsx(BreadcrumbPage, { children: path.name })) }), _jsx(BreadcrumbSeparator, {})] }, index))) }) }));
5
+ const last = paths.length - 1;
6
+ return (_jsx(BaseBreadcrumb, { children: _jsx(BreadcrumbList, { children: paths.map((path, index) => {
7
+ /**
8
+ * ⛔ THE LAST CRUMB IS THE PAGE, whatever the caller passed.
9
+ *
10
+ * The page marker used to be a side effect of the caller omitting
11
+ * `href`, and nothing in `getBreadcrumbPaths` makes the last entry
12
+ * omit one. A trail whose final entry carried an href therefore had
13
+ * no `aria-current="page"` anywhere — a screen reader could not tell
14
+ * which of three links was the page being read — and the crumb was a
15
+ * live link back to the page the reader is already on.
16
+ *
17
+ * ⚠️ AND AN HREF-LESS ANCESTOR IS NOT A SWITCHED-OFF LINK. It used
18
+ * to reach `BreadcrumbPage` too, which hard-codes `role="link"
19
+ * aria-disabled="true"` — the right shape for the page you are ON,
20
+ * a destination that exists and simply is not navigable from here.
21
+ * An ancestor with no href is a grouping label: there is no page
22
+ * behind it, so "Settings, link, dimmed" told the reader a
23
+ * destination was unavailable to them rather than that it was never
24
+ * a destination, and inflated the link count of the trail. It is
25
+ * plain text now, and only the last crumb is the page.
26
+ */
27
+ const isLast = index === last;
28
+ return (_jsxs(React.Fragment, { children: [_jsx(BreadcrumbItem, { children: path.href && !isLast ? (_jsx(BreadcrumbLink, { href: path.href, children: path.name })) : isLast ? (_jsx(BreadcrumbPage, { children: path.name })) : (_jsx("span", { className: "breadcrumb-page", children: path.name })) }), !isLast && _jsx(BreadcrumbSeparator, {})] }, index));
29
+ }) }) }));
6
30
  };