@lerianstudio/sindarian-ui 1.4.0-beta.8 → 2.0.0-beta.2

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 (129) hide show
  1. package/dist/components/card/account-card/index.d.ts.map +1 -1
  2. package/dist/components/card/account-card/index.js +1 -1
  3. package/dist/components/card/entity-card/index.js +3 -3
  4. package/dist/components/dialog/confirmation-dialog/index.d.ts +36 -5
  5. package/dist/components/dialog/confirmation-dialog/index.d.ts.map +1 -1
  6. package/dist/components/dialog/confirmation-dialog/index.js +2 -2
  7. package/dist/components/entity-box/index.js +1 -1
  8. package/dist/components/entity-data-table/index.d.ts.map +1 -1
  9. package/dist/components/entity-data-table/index.js +1 -1
  10. package/dist/components/form/file-upload-field/index.d.ts +11 -2
  11. package/dist/components/form/file-upload-field/index.d.ts.map +1 -1
  12. package/dist/components/form/file-upload-field/index.js +8 -3
  13. package/dist/components/form/input-field/index.d.ts +41 -2
  14. package/dist/components/form/input-field/index.d.ts.map +1 -1
  15. package/dist/components/form/input-field/index.js +73 -15
  16. package/dist/components/form/select-field/index.d.ts +9 -1
  17. package/dist/components/form/select-field/index.d.ts.map +1 -1
  18. package/dist/components/form/select-field/index.js +2 -2
  19. package/dist/components/page-header/index.d.ts.map +1 -1
  20. package/dist/components/page-header/index.js +2 -2
  21. package/dist/components/ui/autocomplete/index.d.ts +34 -6
  22. package/dist/components/ui/autocomplete/index.d.ts.map +1 -1
  23. package/dist/components/ui/autocomplete/index.js +72 -16
  24. package/dist/components/ui/badge/index.d.ts.map +1 -1
  25. package/dist/components/ui/badge/index.js +11 -1
  26. package/dist/components/ui/button/styles.css +31 -1
  27. package/dist/components/ui/calendar/index.d.ts.map +1 -1
  28. package/dist/components/ui/calendar/index.js +7 -1
  29. package/dist/components/ui/card/index.d.ts.map +1 -1
  30. package/dist/components/ui/card/index.js +1 -1
  31. package/dist/components/ui/dropdown-menu/index.d.ts.map +1 -1
  32. package/dist/components/ui/dropdown-menu/index.js +4 -1
  33. package/dist/components/ui/field/index.d.ts.map +1 -1
  34. package/dist/components/ui/field/index.js +5 -2
  35. package/dist/components/ui/file-upload/index.d.ts +76 -9
  36. package/dist/components/ui/file-upload/index.d.ts.map +1 -1
  37. package/dist/components/ui/file-upload/index.js +47 -12
  38. package/dist/components/ui/form.d.ts +4 -0
  39. package/dist/components/ui/form.d.ts.map +1 -1
  40. package/dist/components/ui/form.js +53 -8
  41. package/dist/components/ui/multiple-select/index.js +2 -2
  42. package/dist/components/ui/progress/index.d.ts +15 -1
  43. package/dist/components/ui/progress/index.d.ts.map +1 -1
  44. package/dist/components/ui/progress/index.js +25 -2
  45. package/dist/components/ui/sidebar/sidebar-expand-button.d.ts.map +1 -1
  46. package/dist/components/ui/sidebar/sidebar-expand-button.js +17 -1
  47. package/dist/components/ui/toggle-group/index.d.ts +1 -1
  48. package/dist/components/ui/toggle-group/index.d.ts.map +1 -1
  49. package/dist/components/ui/toggle-group/index.js +73 -4
  50. package/dist/components/ui/tooltip/index.d.ts +15 -0
  51. package/dist/components/ui/tooltip/index.d.ts.map +1 -1
  52. package/dist/components/ui/tooltip/index.js +16 -1
  53. package/dist/domain/blotter/index.d.ts +4 -2
  54. package/dist/domain/blotter/index.d.ts.map +1 -1
  55. package/dist/domain/delinquency-aging/aging-buckets.d.ts.map +1 -1
  56. package/dist/domain/delinquency-aging/aging-buckets.js +8 -3
  57. package/dist/domain/delinquency-aging/index.d.ts.map +1 -1
  58. package/dist/domain/delinquency-aging/index.js +8 -3
  59. package/dist/domain/key-id/index.js +1 -1
  60. package/dist/domain/status-rail/index.js +1 -1
  61. package/dist/domain/threshold-gauge/index.d.ts.map +1 -1
  62. package/dist/domain/threshold-gauge/index.js +9 -5
  63. package/dist/enterprise/stat-card/index.d.ts.map +1 -1
  64. package/dist/enterprise/stat-card/index.js +12 -4
  65. package/dist/esm/components/card/account-card/index.d.ts.map +1 -1
  66. package/dist/esm/components/card/account-card/index.js +1 -1
  67. package/dist/esm/components/card/entity-card/index.js +3 -3
  68. package/dist/esm/components/dialog/confirmation-dialog/index.d.ts +36 -5
  69. package/dist/esm/components/dialog/confirmation-dialog/index.d.ts.map +1 -1
  70. package/dist/esm/components/dialog/confirmation-dialog/index.js +2 -2
  71. package/dist/esm/components/entity-box/index.js +1 -1
  72. package/dist/esm/components/entity-data-table/index.d.ts.map +1 -1
  73. package/dist/esm/components/entity-data-table/index.js +1 -1
  74. package/dist/esm/components/form/file-upload-field/index.d.ts +11 -2
  75. package/dist/esm/components/form/file-upload-field/index.d.ts.map +1 -1
  76. package/dist/esm/components/form/file-upload-field/index.js +8 -3
  77. package/dist/esm/components/form/input-field/index.d.ts +41 -2
  78. package/dist/esm/components/form/input-field/index.d.ts.map +1 -1
  79. package/dist/esm/components/form/input-field/index.js +73 -15
  80. package/dist/esm/components/form/select-field/index.d.ts +9 -1
  81. package/dist/esm/components/form/select-field/index.d.ts.map +1 -1
  82. package/dist/esm/components/form/select-field/index.js +2 -2
  83. package/dist/esm/components/page-header/index.d.ts.map +1 -1
  84. package/dist/esm/components/page-header/index.js +2 -2
  85. package/dist/esm/components/ui/autocomplete/index.d.ts +34 -6
  86. package/dist/esm/components/ui/autocomplete/index.d.ts.map +1 -1
  87. package/dist/esm/components/ui/autocomplete/index.js +72 -16
  88. package/dist/esm/components/ui/badge/index.d.ts.map +1 -1
  89. package/dist/esm/components/ui/badge/index.js +11 -1
  90. package/dist/esm/components/ui/calendar/index.d.ts.map +1 -1
  91. package/dist/esm/components/ui/calendar/index.js +8 -2
  92. package/dist/esm/components/ui/card/index.d.ts.map +1 -1
  93. package/dist/esm/components/ui/card/index.js +1 -1
  94. package/dist/esm/components/ui/dropdown-menu/index.d.ts.map +1 -1
  95. package/dist/esm/components/ui/dropdown-menu/index.js +4 -1
  96. package/dist/esm/components/ui/field/index.d.ts.map +1 -1
  97. package/dist/esm/components/ui/field/index.js +5 -2
  98. package/dist/esm/components/ui/file-upload/index.d.ts +76 -9
  99. package/dist/esm/components/ui/file-upload/index.d.ts.map +1 -1
  100. package/dist/esm/components/ui/file-upload/index.js +47 -13
  101. package/dist/esm/components/ui/form.d.ts +4 -0
  102. package/dist/esm/components/ui/form.d.ts.map +1 -1
  103. package/dist/esm/components/ui/form.js +53 -8
  104. package/dist/esm/components/ui/multiple-select/index.js +2 -2
  105. package/dist/esm/components/ui/progress/index.d.ts +15 -1
  106. package/dist/esm/components/ui/progress/index.d.ts.map +1 -1
  107. package/dist/esm/components/ui/progress/index.js +25 -2
  108. package/dist/esm/components/ui/sidebar/sidebar-expand-button.d.ts.map +1 -1
  109. package/dist/esm/components/ui/sidebar/sidebar-expand-button.js +17 -1
  110. package/dist/esm/components/ui/toggle-group/index.d.ts +1 -1
  111. package/dist/esm/components/ui/toggle-group/index.d.ts.map +1 -1
  112. package/dist/esm/components/ui/toggle-group/index.js +73 -4
  113. package/dist/esm/components/ui/tooltip/index.d.ts +15 -0
  114. package/dist/esm/components/ui/tooltip/index.d.ts.map +1 -1
  115. package/dist/esm/components/ui/tooltip/index.js +16 -1
  116. package/dist/esm/domain/blotter/index.d.ts +4 -2
  117. package/dist/esm/domain/blotter/index.d.ts.map +1 -1
  118. package/dist/esm/domain/delinquency-aging/aging-buckets.d.ts.map +1 -1
  119. package/dist/esm/domain/delinquency-aging/aging-buckets.js +8 -3
  120. package/dist/esm/domain/delinquency-aging/index.d.ts.map +1 -1
  121. package/dist/esm/domain/delinquency-aging/index.js +8 -3
  122. package/dist/esm/domain/key-id/index.js +1 -1
  123. package/dist/esm/domain/status-rail/index.js +1 -1
  124. package/dist/esm/domain/threshold-gauge/index.d.ts.map +1 -1
  125. package/dist/esm/domain/threshold-gauge/index.js +9 -5
  126. package/dist/esm/enterprise/stat-card/index.d.ts.map +1 -1
  127. package/dist/esm/enterprise/stat-card/index.js +12 -4
  128. package/dist/globals.css +24 -1
  129. package/package.json +1 -1
@@ -75,12 +75,31 @@ const useAutocomplete = () => {
75
75
  }
76
76
  return context;
77
77
  };
78
- function AutocompleteTrigger({ ref, className, onClear, children }) {
78
+ /**
79
+ * The text a value reads as.
80
+ *
81
+ * An item registers a label only when it HAS one to give: a string child, or an
82
+ * explicit `label` when its children are markup. Everything downstream renders
83
+ * this map as text (the input's value, a chip's `aria-label`), so a value with
84
+ * no registered label falls back to the value itself. The alternatives are both
85
+ * defects that shipped: `undefined` drops a controlled input to uncontrolled,
86
+ * and a React element interpolates into a template literal as "[object
87
+ * Object]".
88
+ */
89
+ function labelOf(options, value) {
90
+ return options[value] ?? value;
91
+ }
92
+ function AutocompleteTrigger({ ref, className, onClear, clearLabel = 'Clear', children }) {
79
93
  const _ref = react_1.default.useRef(null);
80
94
  const { open, readOnly, setOpen, disabled, value, inputRef, handleClear } = useAutocomplete();
81
95
  react_1.default.useImperativeHandle(ref, () => _ref.current);
82
96
  const isNoSelection = disabled || readOnly || value.length < 1;
83
- return ((0, jsx_runtime_1.jsxs)("div", { ref: _ref, className: (0, utils_1.cn)('bg-background ring-offset-background placeholder:text-shadcn-400 data-[disabled=true]:bg-shadcn-100 data-[read-only=true]:bg-shadcn-100 border-input flex rounded-md border text-sm focus:outline-hidden focus-visible:outline-hidden data-[disabled=true]:cursor-not-allowed data-[disabled=true]:opacity-50 data-[read-only=true]:opacity-50 md:text-sm [&>span]:line-clamp-1', {
97
+ return ((0, jsx_runtime_1.jsxs)("div", { ref: _ref, className: (0, utils_1.cn)(
98
+ // `border-input-border`, NOT `border-input`: `--input` is a SURFACE
99
+ // token and is white in light, so the border it paints is invisible
100
+ // against the field's own `bg-background`. This is the same edge
101
+ // MultipleSelect and DateRangePicker draw, and the same token.
102
+ 'bg-background ring-offset-background placeholder:text-input-placeholder data-[disabled=true]:bg-shadcn-100 data-[read-only=true]:bg-shadcn-100 border-input-border flex rounded-md border text-sm focus:outline-hidden focus-visible:outline-hidden data-[disabled=true]:cursor-not-allowed data-[disabled=true]:opacity-50 data-[read-only=true]:opacity-50 md:text-sm [&>span]:line-clamp-1', {
84
103
  'h-9': value.length === 0,
85
104
  'min-h-9': value.length > 0,
86
105
  'cursor-text': !disabled && value.length !== 0
@@ -96,11 +115,19 @@ function AutocompleteTrigger({ ref, className, onClear, children }) {
96
115
  inputRef?.current?.focus();
97
116
  }
98
117
  setOpen(!open);
99
- }, "data-disabled": disabled, "data-read-only": readOnly, children: [(0, jsx_runtime_1.jsx)("div", { className: "flex grow flex-wrap gap-1 px-3 py-2", children: children }), (0, jsx_runtime_1.jsxs)("div", { className: "flex shrink-0 items-center justify-end", children: [(0, jsx_runtime_1.jsx)("button", { type: "button", className: (0, utils_1.cn)(isNoSelection && 'hidden'), children: (0, jsx_runtime_1.jsx)(lucide_react_1.X, { className: "text-muted-foreground mx-2 h-4 cursor-pointer", onClick: (event) => {
118
+ }, "data-disabled": disabled, "data-read-only": readOnly, children: [(0, jsx_runtime_1.jsx)("div", { className: "flex grow flex-wrap gap-1 px-3 py-2", children: children }), (0, jsx_runtime_1.jsxs)("div", { className: "flex shrink-0 items-center justify-end", children: [(0, jsx_runtime_1.jsx)("button", { type: "button", "aria-label": clearLabel, className: (0, utils_1.cn)('cursor-pointer', isNoSelection && 'hidden'), onKeyDown: (event) => {
119
+ // cmdk's Command root calls preventDefault() on EVERY Enter that
120
+ // reaches it, which cancels this button's implicit activation, so
121
+ // the keydown must not get there. Enter only: the root's other
122
+ // keys (Escape, the arrows) still belong to the panel.
123
+ if (event.key === 'Enter') {
100
124
  event.stopPropagation();
101
- handleClear();
102
- onClear?.();
103
- } }) }), (0, jsx_runtime_1.jsx)(separator_1.Separator, { orientation: "vertical", className: (0, utils_1.cn)(isNoSelection && 'hidden') }), (0, jsx_runtime_1.jsx)(SelectPrimitive.Icon, { className: "cursor-pointer data-[disabled=true]:cursor-not-allowed data-[read-only=true]:cursor-default", "data-disabled": disabled, "data-read-only": readOnly, children: (0, jsx_runtime_1.jsx)(lucide_react_1.ChevronDown, { className: "mx-3 my-2 h-4 w-4 opacity-50" }) })] })] }));
125
+ }
126
+ }, onClick: (event) => {
127
+ event.stopPropagation();
128
+ handleClear();
129
+ onClear?.();
130
+ }, children: (0, jsx_runtime_1.jsx)(lucide_react_1.X, { "aria-hidden": true, className: "text-muted-foreground mx-2 h-4" }) }), (0, jsx_runtime_1.jsx)(separator_1.Separator, { orientation: "vertical", className: (0, utils_1.cn)(isNoSelection && 'hidden') }), (0, jsx_runtime_1.jsx)(SelectPrimitive.Icon, { className: "cursor-pointer data-[disabled=true]:cursor-not-allowed data-[read-only=true]:cursor-default", "data-disabled": disabled, "data-read-only": readOnly, children: (0, jsx_runtime_1.jsx)(lucide_react_1.ChevronDown, { className: "mx-3 my-2 h-4 w-4 opacity-50" }) })] })] }));
104
131
  }
105
132
  function AutocompleteValue({ ref, className, value: valueProp, onValueChange, onBlur, ...props }) {
106
133
  const [search, setSearch] = react_1.default.useState(valueProp);
@@ -115,7 +142,7 @@ function AutocompleteValue({ ref, className, value: valueProp, onValueChange, on
115
142
  handleValueChange('');
116
143
  return;
117
144
  }
118
- handleValueChange(showValue ? v : options[v]);
145
+ handleValueChange(showValue ? v : labelOf(options, v));
119
146
  };
120
147
  const handleValueChange = (value) => {
121
148
  setSearch(value);
@@ -134,21 +161,29 @@ function AutocompleteValue({ ref, className, value: valueProp, onValueChange, on
134
161
  if (v === '') {
135
162
  return;
136
163
  }
137
- setSearch(showValue ? v : options[v]);
164
+ setSearch(showValue ? v : labelOf(options, v));
138
165
  }, [options]);
139
166
  react_1.default.useEffect(() => {
140
167
  updateSearch(value);
141
168
  }, [value]);
142
169
  return ((0, jsx_runtime_1.jsx)(cmdk_1.Command.Input, { ...props, ref: inputRef, value: search, onValueChange: handleValueChange, disabled: disabled, readOnly: readOnly, className: (0, utils_1.cn)('placeholder:text-muted-foreground w-full bg-transparent outline-hidden read-only:opacity-50 read-only:placeholder:opacity-0 focus:outline-hidden disabled:cursor-not-allowed disabled:opacity-50 disabled:placeholder:opacity-0', className), onBlur: handleBlur }));
143
170
  }
144
- function AutocompleteMultipleValue({ ref, className, ...props }) {
171
+ function AutocompleteMultipleValue({ ref, className, clearLabel = 'Clear', ...props }) {
145
172
  const { value, disabled, handleChange, options, showValue, inputRef } = useAutocomplete();
146
173
  react_1.default.useImperativeHandle(ref, () => inputRef.current);
147
174
  return ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [options &&
148
- value?.map((value) => ((0, jsx_runtime_1.jsxs)(badge_1.Badge, { variant: "secondary", className: (0, utils_1.cn)('data-disabled:bg-muted-foreground data-fixed:bg-muted-foreground data-disabled:text-muted data-fixed:text-muted data-disabled:hover:bg-muted-foreground data-fixed:hover:bg-muted-foreground'), children: [showValue ? value : options[value], (0, jsx_runtime_1.jsx)("button", { type: "button", className: (0, utils_1.cn)('ring-offset-background focus:ring-ring ml-1 rounded-full outline-hidden focus:ring-2 focus:ring-offset-2', disabled && 'hidden'), children: (0, jsx_runtime_1.jsx)(lucide_react_1.X, { className: "text-muted-foreground hover:text-foreground h-3 w-3", onClick: (event) => {
175
+ value?.map((value) => ((0, jsx_runtime_1.jsxs)(badge_1.Badge, { variant: "secondary", className: (0, utils_1.cn)('data-disabled:bg-muted-foreground data-fixed:bg-muted-foreground data-disabled:text-muted data-fixed:text-muted data-disabled:hover:bg-muted-foreground data-fixed:hover:bg-muted-foreground'), children: [showValue ? value : labelOf(options, value), (0, jsx_runtime_1.jsx)("button", { type: "button", "aria-label": `${clearLabel} ${showValue ? value : labelOf(options, value)}`, className: (0, utils_1.cn)('ring-offset-background focus:ring-ring ml-1 rounded-full outline-hidden focus:ring-2 focus:ring-offset-2', disabled && 'hidden'), onKeyDown: (event) => {
176
+ // cmdk's Command root calls preventDefault() on EVERY Enter that
177
+ // reaches it, which cancels this button's implicit activation, so
178
+ // the keydown must not get there. Enter only: the root's other
179
+ // keys (Escape, the arrows) still belong to the panel.
180
+ if (event.key === 'Enter') {
149
181
  event.stopPropagation();
150
- handleChange(value);
151
- } }) })] }, value))), (0, jsx_runtime_1.jsx)(cmdk_1.Command.Input, { ...props, ref: inputRef, disabled: disabled, className: (0, utils_1.cn)('placeholder:text-muted-foreground w-full bg-transparent outline-hidden focus:outline-hidden disabled:cursor-not-allowed disabled:opacity-50 disabled:placeholder:opacity-0', className) })] }));
182
+ }
183
+ }, onClick: (event) => {
184
+ event.stopPropagation();
185
+ handleChange(value);
186
+ }, children: (0, jsx_runtime_1.jsx)(lucide_react_1.X, { "aria-hidden": true, className: "text-muted-foreground hover:text-foreground h-3 w-3" }) })] }, value))), (0, jsx_runtime_1.jsx)(cmdk_1.Command.Input, { ...props, ref: inputRef, disabled: disabled, className: (0, utils_1.cn)('placeholder:text-muted-foreground w-full bg-transparent outline-hidden focus:outline-hidden disabled:cursor-not-allowed disabled:opacity-50 disabled:placeholder:opacity-0', className) })] }));
152
187
  }
153
188
  function AutocompleteEmpty({ className, ...props }) {
154
189
  const render = (0, cmdk_1.useCommandState)((state) => state.filtered.count === 0);
@@ -200,9 +235,16 @@ function AutocompleteContent({ className, position = 'popper', side = 'bottom',
200
235
  child.type.displayName === 'AutocompleteItem') {
201
236
  // Check if the item was proper filled with a value prop
202
237
  if (child.props.value) {
203
- addOption({
204
- [child.props.value]: child.props.children
205
- });
238
+ // NOT `children as string`. That cast is how a React ELEMENT got into
239
+ // a map every reader treats as text. An item with markup children
240
+ // registers nothing unless it declares a `label`, and `labelOf` reads
241
+ // the absence as "use the value".
242
+ const { label, children: itemChildren } = child.props;
243
+ const text = label ??
244
+ (typeof itemChildren === 'string' ? itemChildren : undefined);
245
+ if (text !== undefined) {
246
+ addOption({ [child.props.value]: text });
247
+ }
206
248
  }
207
249
  return;
208
250
  }
@@ -258,7 +300,11 @@ function AutocompleteContent({ className, position = 'popper', side = 'bottom',
258
300
  function AutocompleteGroup({ className, ...props }) {
259
301
  return ((0, jsx_runtime_1.jsx)(cmdk_1.Command.Group, { "data-slot": "autocomplete-group", className: (0, utils_1.cn)('text-foreground [&_[cmdk-group-heading]]:text-muted-foreground overflow-hidden p-1 [&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:py-1.5 [&_[cmdk-group-heading]]:text-xs [&_[cmdk-group-heading]]:font-medium', className), ...props }));
260
302
  }
261
- function AutocompleteItem({ className, value, onSelect, ...props }) {
303
+ function AutocompleteItem({ className, value,
304
+ // Read off the element by `AutocompleteContent._searchChildren`, never
305
+ // rendered: destructured here only to keep it out of `...props` and off the
306
+ // div cmdk spreads them onto.
307
+ label: _label, onSelect, ...props }) {
262
308
  const { handleChange } = useAutocomplete();
263
309
  return ((0, jsx_runtime_1.jsx)(cmdk_1.Command.Item, { "data-slot": "autocomplete-item", value: value, className: (0, utils_1.cn)("data-[selected='true']:bg-accent data-[selected=true]:text-accent-foreground relative flex w-full cursor-default items-center gap-2 rounded-sm py-1.5 pr-8 pl-2 text-sm outline-hidden select-none data-[disabled=true]:pointer-events-none data-[disabled=true]:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0", className), ...props, onMouseDown: (e) => {
264
310
  e.preventDefault();
@@ -268,6 +314,16 @@ function AutocompleteItem({ className, value, onSelect, ...props }) {
268
314
  onSelect?.(value);
269
315
  } }));
270
316
  }
317
+ /**
318
+ * ⛔ LOAD-BEARING, NOT A DEBUG LABEL. `AutocompleteContent._searchChildren`
319
+ * recognises an option by `child.type.displayName === 'AutocompleteItem'`, and
320
+ * a function component carries no displayName unless it is assigned one. It
321
+ * never was, so the walk matched nothing and the option registry stayed empty
322
+ * for every consumer: a multiple-select chip rendered `options[value]` as
323
+ * `undefined` (an empty chip), and `AutocompleteValue.updateSearch` set the
324
+ * input to `undefined` on every selection outside `showValue` mode.
325
+ */
326
+ AutocompleteItem.displayName = 'AutocompleteItem';
271
327
  function Autocomplete({ ref, value, defaultValue, onValueChange, onClear, className, open: openProp, onOpenChange, showValue, readOnly, disabled, multiple, onKeyDown, children, ...props }) {
272
328
  const _ref = react_1.default.useRef(null);
273
329
  const [open, _setOpen] = react_1.default.useState(openProp);
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/components/ui/badge/index.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAC9B,OAAO,EAAO,KAAK,YAAY,EAAE,MAAM,0BAA0B,CAAA;AAKjE;;;;;;GAMG;AACH;;;;;;;GAOG;AACH,QAAA,MAAM,aAAa;;;8EA6ClB,CAAA;AAED,MAAM,MAAM,UAAU,GAAG,KAAK,CAAC,cAAc,CAAC,MAAM,CAAC,GACnD,YAAY,CAAC,OAAO,aAAa,CAAC,GAAG;IAAE,OAAO,CAAC,EAAE,OAAO,CAAA;CAAE,CAAA;AAE5D,iBAAS,KAAK,CAAC,EAAE,SAAS,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,GAAG,KAAK,EAAE,EAAE,UAAU,qBAUzE;AAED,OAAO,EAAE,KAAK,EAAE,aAAa,EAAE,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/components/ui/badge/index.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAC9B,OAAO,EAAO,KAAK,YAAY,EAAE,MAAM,0BAA0B,CAAA;AAKjE;;;;;;GAMG;AACH;;;;;;;GAOG;AACH,QAAA,MAAM,aAAa;;;8EA6ClB,CAAA;AAED,MAAM,MAAM,UAAU,GAAG,KAAK,CAAC,cAAc,CAAC,MAAM,CAAC,GACnD,YAAY,CAAC,OAAO,aAAa,CAAC,GAAG;IAAE,OAAO,CAAC,EAAE,OAAO,CAAA;CAAE,CAAA;AAE5D,iBAAS,KAAK,CAAC,EAAE,SAAS,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,GAAG,KAAK,EAAE,EAAE,UAAU,qBAsBzE;AAED,OAAO,EAAE,KAAK,EAAE,aAAa,EAAE,CAAA"}
@@ -57,5 +57,15 @@ const badgeVariants = (0, class_variance_authority_1.cva)('inline-flex items-cen
57
57
  exports.badgeVariants = badgeVariants;
58
58
  function Badge({ className, variant, size, asChild, ...props }) {
59
59
  const Comp = asChild ? react_slot_1.Slot : 'span';
60
- return ((0, jsx_runtime_1.jsx)(Comp, { "data-slot": "badge", className: (0, utils_1.cn)(badgeVariants({ variant, size }), className), ...props }));
60
+ /**
61
+ * A bare `span` is generic, and ARIA prohibits an accessible name on a
62
+ * generic element: a caller's `aria-label` was dropped by assistive tech and
63
+ * flagged by axe as `aria-prohibited-attr`. `role="img"` is the smallest role
64
+ * that permits the name and lets it stand in for the visible text, which is
65
+ * exactly what a labelled span badge wants. Unlabelled badges stay generic;
66
+ * `asChild` keeps the child's native role; and a caller's own `role` still wins
67
+ * because it arrives with the spread below.
68
+ */
69
+ const labelled = props['aria-label'] !== undefined || props['aria-labelledby'] !== undefined;
70
+ return ((0, jsx_runtime_1.jsx)(Comp, { "data-slot": "badge", role: !asChild && labelled ? 'img' : undefined, className: (0, utils_1.cn)(badgeVariants({ variant, size }), className), ...props }));
61
71
  }
@@ -34,7 +34,12 @@
34
34
  }
35
35
 
36
36
  .button-plain {
37
- @apply bg-transparent text-inherit shadow-none hover:bg-black/5;
37
+ /* The dark counterpart is not optional: black/5 over a dark ground is
38
+ invisible, so the hover affordance simply vanished in dark mode. Same
39
+ /5-light, /10-dark pairing the kit already uses for translucent state
40
+ fills (`ui/field`: `has-data-checked:bg-primary/5
41
+ dark:has-data-checked:bg-primary/10`). */
42
+ @apply bg-transparent text-inherit shadow-none hover:bg-black/5 dark:hover:bg-white/10;
38
43
  }
39
44
 
40
45
  .button-primary {
@@ -63,6 +68,31 @@
63
68
  @apply !text-button-link !h-4 !p-0 !px-0 !py-0 underline shadow-none;
64
69
  }
65
70
 
71
+ /*
72
+ WCAG 2.5.8 (AA) asks a pointer target for 24 by 24 CSS px. This variant pins
73
+ its box at 16, and pins it with `!important` on the height and all three
74
+ padding axes — so unlike an ordinary too-small target, no consumer can
75
+ correct it at the call site. That is what makes it a library defect rather
76
+ than a call-site one; the 16px itself is not new.
77
+
78
+ The hit area grows instead of the box. `.button-base` is already
79
+ `position: relative`, so an inset pseudo-element takes the target to 24px
80
+ tall at zero layout cost: the element's own box, and every line box around
81
+ it, stay at 16. Growing the box was the alternative and it was rejected —
82
+ link buttons sit inside running text, where a taller box reflows the
83
+ sentence, and in table cells and filter bars, where it shifts the row.
84
+
85
+ Horizontal size is left to the label. SC 2.5.8 exempts a target whose size
86
+ is constrained by the line-height of the text around it, and stealing
87
+ horizontal space from adjacent words would take clicks that belong to them.
88
+
89
+ Overridable: Tailwind's `utilities` layer wins over `components`, so a
90
+ consumer that needs the band gone writes `before:inset-y-0`.
91
+ */
92
+ .button-link::before {
93
+ @apply absolute inset-x-0 -inset-y-1 content-[''];
94
+ }
95
+
66
96
  .button-disabled {
67
97
  @apply disabled:pointer-events-none disabled:border-transparent disabled:opacity-50 disabled:shadow-none;
68
98
  }
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/components/ui/calendar/index.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAC9B,OAAO,EACL,SAAS,EAET,KAAK,SAAS,EACf,MAAM,kBAAkB,CAAA;AAGzB,OAAO,EAAE,MAAM,EAAkB,MAAM,wBAAwB,CAAA;AAO/D,iBAAS,QAAQ,CAAC,EAChB,SAAS,EACT,UAAU,EACV,eAAsB,EACtB,aAAuB,EACvB,aAAuB,EACvB,UAAU,EACV,UAAU,EACV,GAAG,KAAK,EACT,EAAE,KAAK,CAAC,cAAc,CAAC,OAAO,SAAS,CAAC,GAAG;IAC1C,aAAa,CAAC,EAAE,KAAK,CAAC,cAAc,CAAC,OAAO,MAAM,CAAC,CAAC,SAAS,CAAC,CAAA;CAC/D,qBA0JA;AAED,iBAAS,iBAAiB,CAAC,EACzB,SAAS,EACT,GAAG,EACH,SAAS,EACT,GAAG,KAAK,EACT,EAAE,KAAK,CAAC,cAAc,CAAC,OAAO,SAAS,CAAC,qBAqCxC;AAED,OAAO,EAAE,QAAQ,EAAE,iBAAiB,EAAE,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/components/ui/calendar/index.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAC9B,OAAO,EACL,SAAS,EAGT,KAAK,SAAS,EACf,MAAM,kBAAkB,CAAA;AAGzB,OAAO,EAAE,MAAM,EAAkB,MAAM,wBAAwB,CAAA;AAO/D,iBAAS,QAAQ,CAAC,EAChB,SAAS,EACT,UAAU,EACV,eAAsB,EACtB,aAAuB,EACvB,aAAuB,EACvB,UAAU,EACV,UAAU,EACV,GAAG,KAAK,EACT,EAAE,KAAK,CAAC,cAAc,CAAC,OAAO,SAAS,CAAC,GAAG;IAC1C,aAAa,CAAC,EAAE,KAAK,CAAC,cAAc,CAAC,OAAO,MAAM,CAAC,CAAC,SAAS,CAAC,CAAA;CAC/D,qBAgKA;AAED,iBAAS,iBAAiB,CAAC,EACzB,SAAS,EACT,GAAG,EACH,SAAS,EACT,GAAG,KAAK,EACT,EAAE,KAAK,CAAC,cAAc,CAAC,OAAO,SAAS,CAAC,qBAqCxC;AAED,OAAO,EAAE,QAAQ,EAAE,iBAAiB,EAAE,CAAA"}
@@ -45,7 +45,13 @@ const lucide_react_1 = require("lucide-react");
45
45
  function Calendar({ className, classNames, showOutsideDays = true, captionLayout = 'label', buttonVariant = 'plain', formatters, components, ...props }) {
46
46
  const defaultClassNames = (0, react_day_picker_1.getDefaultClassNames)();
47
47
  return ((0, jsx_runtime_1.jsx)(react_day_picker_1.DayPicker, { showOutsideDays: showOutsideDays, className: (0, utils_1.cn)('bg-background group/calendar p-2 [--cell-radius:var(--radius-md)] [--cell-size:34px] [--nav-size:28px] [[data-slot=card-content]_&]:bg-transparent [[data-slot=popover-content]_&]:bg-transparent', String.raw `rtl:**:[.rdp-button\_next>svg]:rotate-180`, String.raw `rtl:**:[.rdp-button\_previous>svg]:rotate-180`, className), captionLayout: captionLayout, formatters: {
48
- formatMonthDropdown: (date) => date.toLocaleString('default', { month: 'short' }),
48
+ // react-day-picker's own default is dateLib.format(month, 'LLLL'), the
49
+ // full month name in the locale it was given. Shortening it has to go
50
+ // through the same dateLib: Date.toLocaleString('default') reads the
51
+ // runtime locale instead, so the dropdown fell back to English while
52
+ // the weekdays and the caption around it stayed in the consumer's
53
+ // language. 'LLL' is the abbreviated form of the same standalone month.
54
+ formatMonthDropdown: (month, dateLib = react_day_picker_1.defaultDateLib) => dateLib.format(month, 'LLL'),
49
55
  ...formatters
50
56
  }, classNames: {
51
57
  root: (0, utils_1.cn)('w-fit', defaultClassNames.root),
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/components/ui/card/index.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAI9B,iBAAS,IAAI,CAAC,EAAE,SAAS,EAAE,GAAG,KAAK,EAAE,EAAE,KAAK,CAAC,cAAc,CAAC,KAAK,CAAC,qBAWjE;AAED,iBAAS,UAAU,CAAC,EAAE,SAAS,EAAE,GAAG,KAAK,EAAE,EAAE,KAAK,CAAC,cAAc,CAAC,KAAK,CAAC,qBAQvE;AAED,KAAK,cAAc,GAAG,KAAK,CAAC,cAAc,CAAC,IAAI,CAAC,GAAG;IACjD;;;;OAIG;IACH,EAAE,CAAC,EAAE,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,CAAA;CAC7C,CAAA;AAED,iBAAS,SAAS,CAAC,EAAE,SAAS,EAAE,EAAE,EAAE,IAAW,EAAE,GAAG,KAAK,EAAE,EAAE,cAAc,qBAW1E;AAED,iBAAS,eAAe,CAAC,EAAE,SAAS,EAAE,GAAG,KAAK,EAAE,EAAE,KAAK,CAAC,cAAc,CAAC,GAAG,CAAC,qBAQ1E;AAED,iBAAS,UAAU,CAAC,EAAE,SAAS,EAAE,GAAG,KAAK,EAAE,EAAE,KAAK,CAAC,cAAc,CAAC,KAAK,CAAC,qBAWvE;AAED,iBAAS,WAAW,CAAC,EAAE,SAAS,EAAE,GAAG,KAAK,EAAE,EAAE,KAAK,CAAC,cAAc,CAAC,KAAK,CAAC,qBAQxE;AAED,iBAAS,UAAU,CAAC,EAAE,SAAS,EAAE,GAAG,KAAK,EAAE,EAAE,KAAK,CAAC,cAAc,CAAC,KAAK,CAAC,qBAQvE;AAED,OAAO,EACL,IAAI,EACJ,UAAU,EACV,UAAU,EACV,SAAS,EACT,UAAU,EACV,eAAe,EACf,WAAW,EACZ,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/components/ui/card/index.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAI9B,iBAAS,IAAI,CAAC,EAAE,SAAS,EAAE,GAAG,KAAK,EAAE,EAAE,KAAK,CAAC,cAAc,CAAC,KAAK,CAAC,qBAWjE;AAED,iBAAS,UAAU,CAAC,EAAE,SAAS,EAAE,GAAG,KAAK,EAAE,EAAE,KAAK,CAAC,cAAc,CAAC,KAAK,CAAC,qBAQvE;AAED,KAAK,cAAc,GAAG,KAAK,CAAC,cAAc,CAAC,IAAI,CAAC,GAAG;IACjD;;;;OAIG;IACH,EAAE,CAAC,EAAE,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,CAAA;CAC7C,CAAA;AAED,iBAAS,SAAS,CAAC,EAAE,SAAS,EAAE,EAAE,EAAE,IAAW,EAAE,GAAG,KAAK,EAAE,EAAE,cAAc,qBAQ1E;AAED,iBAAS,eAAe,CAAC,EAAE,SAAS,EAAE,GAAG,KAAK,EAAE,EAAE,KAAK,CAAC,cAAc,CAAC,GAAG,CAAC,qBAQ1E;AAED,iBAAS,UAAU,CAAC,EAAE,SAAS,EAAE,GAAG,KAAK,EAAE,EAAE,KAAK,CAAC,cAAc,CAAC,KAAK,CAAC,qBAWvE;AAED,iBAAS,WAAW,CAAC,EAAE,SAAS,EAAE,GAAG,KAAK,EAAE,EAAE,KAAK,CAAC,cAAc,CAAC,KAAK,CAAC,qBAQxE;AAED,iBAAS,UAAU,CAAC,EAAE,SAAS,EAAE,GAAG,KAAK,EAAE,EAAE,KAAK,CAAC,cAAc,CAAC,KAAK,CAAC,qBAQvE;AAED,OAAO,EACL,IAAI,EACJ,UAAU,EACV,UAAU,EACV,SAAS,EACT,UAAU,EACV,eAAe,EACf,WAAW,EACZ,CAAA"}
@@ -16,7 +16,7 @@ function CardHeader({ className, ...props }) {
16
16
  return ((0, jsx_runtime_1.jsx)("div", { "data-slot": "card-header", className: (0, utils_1.cn)('flex flex-col space-y-1.5', className), ...props }));
17
17
  }
18
18
  function CardTitle({ className, as: Comp = 'h3', ...props }) {
19
- return ((0, jsx_runtime_1.jsx)(Comp, { "data-slot": "card-title", className: (0, utils_1.cn)('text-sm leading-none font-medium tracking-tight', className), ...props }));
19
+ return ((0, jsx_runtime_1.jsx)(Comp, { "data-slot": "card-title", className: (0, utils_1.cn)('text-sm leading-none font-medium', className), ...props }));
20
20
  }
21
21
  function CardDescription({ className, ...props }) {
22
22
  return ((0, jsx_runtime_1.jsx)("p", { "data-slot": "card-description", className: (0, utils_1.cn)('text-muted-foreground text-sm', className), ...props }));
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/components/ui/dropdown-menu/index.tsx"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/components/ui/dropdown-menu/index.tsx"],"names":[],"mappings":"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"}
@@ -74,7 +74,10 @@ function DropdownMenuItem({ className, inset, variant = 'default', ...props }) {
74
74
  return ((0, jsx_runtime_1.jsx)(DropdownMenuPrimitive.Item, { "data-slot": "dropdown-menu-item", "data-inset": inset, "data-variant": variant, className: (0, utils_1.cn)('focus:bg-shadcn-100 focus:text-accent-foreground text-muted-foreground relative flex cursor-pointer items-center gap-3 px-3 py-2 text-sm font-medium outline-hidden transition-colors select-none data-disabled:pointer-events-none data-disabled:opacity-50', className), ...props }));
75
75
  }
76
76
  function DropdownMenuItemIcon({ className, children, ...props }) {
77
- return ((0, jsx_runtime_1.jsx)("span", { "data-slot": "dropdown-menu-item-icon", className: (0, utils_1.cn)('text-shadcn-400', className), ...props, children: React.Children.map(React.Children.toArray(children), (child) => React.isValidElement(child)
77
+ return ((0, jsx_runtime_1.jsx)("span", { "data-slot": "dropdown-menu-item-icon",
78
+ // A meaningful glyph inside an interactive item answers to the 3:1 floor
79
+ // of SC 1.4.11. base/400 reads 2.56:1 on the popover surface in light.
80
+ className: (0, utils_1.cn)('text-muted-foreground', className), ...props, children: React.Children.map(React.Children.toArray(children), (child) => React.isValidElement(child)
78
81
  ? React.cloneElement(child, { size: 16 })
79
82
  : child) }));
80
83
  }
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/components/ui/field/index.tsx"],"names":[],"mappings":"AAGA,OAAO,EAAO,KAAK,YAAY,EAAE,MAAM,0BAA0B,CAAA;AAGjE,OAAO,EAAE,KAAK,EAAE,MAAM,uBAAuB,CAAA;AAG7C,iBAAS,QAAQ,CAAC,EAAE,SAAS,EAAE,GAAG,KAAK,EAAE,EAAE,KAAK,CAAC,cAAc,CAAC,UAAU,CAAC,+BAW1E;AAED,iBAAS,WAAW,CAAC,EACnB,SAAS,EACT,OAAkB,EAClB,GAAG,KAAK,EACT,EAAE,KAAK,CAAC,cAAc,CAAC,QAAQ,CAAC,GAAG;IAAE,OAAO,CAAC,EAAE,QAAQ,GAAG,OAAO,CAAA;CAAE,+BAYnE;AAED,iBAAS,UAAU,CAAC,EAAE,SAAS,EAAE,GAAG,KAAK,EAAE,EAAE,KAAK,CAAC,cAAc,CAAC,KAAK,CAAC,+BAWvE;AAED,QAAA,MAAM,aAAa;;8EAgBlB,CAAA;AAED,iBAAS,KAAK,CAAC,EACb,SAAS,EACT,WAAwB,EACxB,GAAG,KAAK,EACT,EAAE,KAAK,CAAC,cAAc,CAAC,KAAK,CAAC,GAAG,YAAY,CAAC,OAAO,aAAa,CAAC,+BAUlE;AAED,iBAAS,YAAY,CAAC,EAAE,SAAS,EAAE,GAAG,KAAK,EAAE,EAAE,KAAK,CAAC,cAAc,CAAC,KAAK,CAAC,+BAWzE;AAED,iBAAS,UAAU,CAAC,EAClB,SAAS,EACT,GAAG,KAAK,EACT,EAAE,KAAK,CAAC,cAAc,CAAC,OAAO,KAAK,CAAC,+BAYpC;AAED,iBAAS,UAAU,CAAC,EAAE,SAAS,EAAE,GAAG,KAAK,EAAE,EAAE,KAAK,CAAC,cAAc,CAAC,KAAK,CAAC,+BAWvE;AAED,iBAAS,gBAAgB,CAAC,EAAE,SAAS,EAAE,GAAG,KAAK,EAAE,EAAE,KAAK,CAAC,cAAc,CAAC,GAAG,CAAC,+BAa3E;AAED,iBAAS,cAAc,CAAC,EACtB,QAAQ,EACR,SAAS,EACT,GAAG,KAAK,EACT,EAAE,KAAK,CAAC,cAAc,CAAC,KAAK,CAAC,GAAG;IAC/B,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAA;CAC3B,+BAsBA;AAED,iBAAS,UAAU,CAAC,EAClB,SAAS,EACT,QAAQ,EACR,MAAM,EACN,GAAG,KAAK,EACT,EAAE,KAAK,CAAC,cAAc,CAAC,KAAK,CAAC,GAAG;IAC/B,MAAM,CAAC,EAAE,KAAK,CAAC;QAAE,OAAO,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,SAAS,CAAC,CAAA;CACjD,sCA0CA;AAED,OAAO,EACL,KAAK,EACL,UAAU,EACV,gBAAgB,EAChB,UAAU,EACV,UAAU,EACV,WAAW,EACX,cAAc,EACd,QAAQ,EACR,YAAY,EACZ,UAAU,EACX,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/components/ui/field/index.tsx"],"names":[],"mappings":"AAGA,OAAO,EAAO,KAAK,YAAY,EAAE,MAAM,0BAA0B,CAAA;AAGjE,OAAO,EAAE,KAAK,EAAE,MAAM,uBAAuB,CAAA;AAG7C,iBAAS,QAAQ,CAAC,EAAE,SAAS,EAAE,GAAG,KAAK,EAAE,EAAE,KAAK,CAAC,cAAc,CAAC,UAAU,CAAC,+BAW1E;AAED,iBAAS,WAAW,CAAC,EACnB,SAAS,EACT,OAAkB,EAClB,GAAG,KAAK,EACT,EAAE,KAAK,CAAC,cAAc,CAAC,QAAQ,CAAC,GAAG;IAAE,OAAO,CAAC,EAAE,QAAQ,GAAG,OAAO,CAAA;CAAE,+BAYnE;AAED,iBAAS,UAAU,CAAC,EAAE,SAAS,EAAE,GAAG,KAAK,EAAE,EAAE,KAAK,CAAC,cAAc,CAAC,KAAK,CAAC,+BAWvE;AAED,QAAA,MAAM,aAAa;;8EAgBlB,CAAA;AAED,iBAAS,KAAK,CAAC,EACb,SAAS,EACT,WAAwB,EACxB,GAAG,KAAK,EACT,EAAE,KAAK,CAAC,cAAc,CAAC,KAAK,CAAC,GAAG,YAAY,CAAC,OAAO,aAAa,CAAC,+BAUlE;AAED,iBAAS,YAAY,CAAC,EAAE,SAAS,EAAE,GAAG,KAAK,EAAE,EAAE,KAAK,CAAC,cAAc,CAAC,KAAK,CAAC,+BAWzE;AAED,iBAAS,UAAU,CAAC,EAClB,SAAS,EACT,GAAG,KAAK,EACT,EAAE,KAAK,CAAC,cAAc,CAAC,OAAO,KAAK,CAAC,+BAYpC;AAED,iBAAS,UAAU,CAAC,EAAE,SAAS,EAAE,GAAG,KAAK,EAAE,EAAE,KAAK,CAAC,cAAc,CAAC,KAAK,CAAC,+BAWvE;AAED,iBAAS,gBAAgB,CAAC,EAAE,SAAS,EAAE,GAAG,KAAK,EAAE,EAAE,KAAK,CAAC,cAAc,CAAC,GAAG,CAAC,+BAa3E;AAED,iBAAS,cAAc,CAAC,EACtB,QAAQ,EACR,SAAS,EACT,GAAG,KAAK,EACT,EAAE,KAAK,CAAC,cAAc,CAAC,KAAK,CAAC,GAAG;IAC/B,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAA;CAC3B,+BAsBA;AAED,iBAAS,UAAU,CAAC,EAClB,SAAS,EACT,QAAQ,EACR,MAAM,EACN,GAAG,KAAK,EACT,EAAE,KAAK,CAAC,cAAc,CAAC,KAAK,CAAC,GAAG;IAC/B,MAAM,CAAC,EAAE,KAAK,CAAC;QAAE,OAAO,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,SAAS,CAAC,CAAA;CACjD,sCA4CA;AAED,OAAO,EACL,KAAK,EACL,UAAU,EACV,gBAAgB,EAChB,UAAU,EACV,UAAU,EACV,WAAW,EACX,cAAc,EACd,QAAQ,EACR,YAAY,EACZ,UAAU,EACX,CAAA"}
@@ -26,7 +26,7 @@ function FieldLegend({ className, variant = 'legend', ...props }) {
26
26
  function FieldGroup({ className, ...props }) {
27
27
  return ((0, jsx_runtime_1.jsx)("div", { "data-slot": "field-group", className: (0, utils_1.cn)('group/field-group @container/field-group flex w-full flex-col gap-5 data-[slot=checkbox-group]:gap-3 [&>[data-slot=field-group]]:gap-4', className), ...props }));
28
28
  }
29
- const fieldVariants = (0, class_variance_authority_1.cva)('data-[invalid=true]:text-destructive gap-2 group/field flex w-full', {
29
+ const fieldVariants = (0, class_variance_authority_1.cva)('data-[invalid=true]:text-system-error-h1a gap-2 group/field flex w-full', {
30
30
  variants: {
31
31
  orientation: {
32
32
  vertical: 'flex-col [&>*]:w-full [&>.sr-only]:w-auto',
@@ -75,5 +75,8 @@ function FieldError({ className, children, errors, ...props }) {
75
75
  if (!content) {
76
76
  return null;
77
77
  }
78
- return ((0, jsx_runtime_1.jsx)("div", { role: "alert", "data-slot": "field-error", className: (0, utils_1.cn)('text-destructive text-sm font-normal', className), ...props, children: content }));
78
+ return ((0, jsx_runtime_1.jsx)("div", { role: "alert", "data-slot": "field-error",
79
+ // The error TEXT token, not `text-destructive`: that is the badge/fill
80
+ // family and measures 3.80:1 as ink on the dark card, under AA.
81
+ className: (0, utils_1.cn)('text-system-error-h1a text-sm font-normal', className), ...props, children: content }));
79
82
  }
@@ -1,9 +1,16 @@
1
1
  /**
2
- * FileUpload — a controlled "pick a file, read it to text, hand back the text"
3
- * primitive. The defining job: select one file (by click, keyboard, or
4
- * drag-and-drop), validate it against an accept filter + a byte ceiling, read
5
- * it as UTF-8 via FileReader.readAsText, and emit `{ file, text }`. Binary/DER
6
- * (.pfx/.p12) is explicitly OUT of scope — readAsText only.
2
+ * FileUpload — a controlled "pick a file, hand it back" primitive. The defining
3
+ * job: select one file (by click, keyboard, or drag-and-drop), validate it
4
+ * against an accept filter + a byte ceiling, and emit `{ file, text }`.
5
+ *
6
+ * `readAs` decides whether the bytes are decoded on the way through. `'text'`
7
+ * (the default) reads UTF-8 via FileReader.readAsText, which is what a PEM/CSV
8
+ * host wants. `'none'` hands the `File` over untouched and is the only mode a
9
+ * BINARY file can use: a PDF, XLSX or PFX put through readAsText decodes into
10
+ * replacement-character garbage, and that garbage string is then retained for
11
+ * as long as the host holds the value — 20 MiB of PDF became a useless 20 MiB
12
+ * string, and the host still had to read the file a second time to get at the
13
+ * real bytes.
7
14
  *
8
15
  * The real `<input type="file">` IS the accessible control: it is `sr-only`
9
16
  * (visually hidden) but focusable and labelable — never `aria-hidden`, never
@@ -24,6 +31,11 @@
24
31
  * a value, so it cannot masquerade as valid. `accept` is validated against
25
32
  * BOTH extension and MIME because the native `accept` attribute is only a
26
33
  * browser hint and is bypassable via drag-drop.
34
+ *
35
+ * A host that already announces the rejection itself — a toast in its own
36
+ * locale, driven off `onError` — silences this one by returning nothing from
37
+ * `labels.error`. Two announcements for one event, in two languages, is the
38
+ * accessibility defect; the wording is not.
27
39
  */
28
40
  import * as React from 'react';
29
41
  export type FileUploadResult = {
@@ -42,6 +54,33 @@ export type FileUploadError = {
42
54
  kind: 'read-failed';
43
55
  file: File;
44
56
  };
57
+ /** How the picked file's bytes are handled on the way to `onSelect`. */
58
+ export type FileUploadReadAs = 'text' | 'none';
59
+ /**
60
+ * Overrides for the fixed English copy. Every field is optional and defaults to
61
+ * the current value, so a consumer that passes nothing renders exactly as
62
+ * before. These four strings were the whole of the component's user-visible
63
+ * text, and none of them was reachable — a pt-BR console rendered an English
64
+ * affordance and handed a screen reader an English accessible name.
65
+ */
66
+ export interface FileUploadLabels {
67
+ /** Emphasised call to action in the empty zone. Defaults to "Choose a file". */
68
+ action?: string;
69
+ /** Trailing hint after the action. Defaults to "or drag and drop". */
70
+ hint?: string;
71
+ /** Accessible name of the clear button. Defaults to "Remove file". */
72
+ remove?: string;
73
+ /**
74
+ * Copy for the component's own `role="alert"` refusal. Receives the rejection
75
+ * so the message can interpolate the cap or the accept filter; use the
76
+ * exported `humanizeSize` to format `maxSizeBytes` the way the chip does.
77
+ *
78
+ * Return `null`, `undefined` or `''` to stay SILENT and leave the
79
+ * announcement to the host's own `onError` handling. Defaults to the
80
+ * built-in English messages.
81
+ */
82
+ error?: (error: FileUploadError) => string | null | undefined;
83
+ }
45
84
  export type FileUploadProps = {
46
85
  /** Comma-separated accept filter, e.g. ".pem,.key" or "text/plain". Mirrors the native input accept. */
47
86
  accept?: string;
@@ -49,7 +88,18 @@ export type FileUploadProps = {
49
88
  maxSizeBytes?: number;
50
89
  /** Controlled selection. `null` = empty. The host owns state. */
51
90
  value?: FileUploadResult | null;
52
- /** Fires on accept (with {file,text}) or clear (null). Reads UTF-8 via FileReader.readAsText. */
91
+ /**
92
+ * How the accepted file is handed over.
93
+ * - `'text'` (default): decode as UTF-8 via FileReader.readAsText and emit
94
+ * `{ file, text }`. Unchanged behaviour for every existing consumer.
95
+ * - `'none'`: skip decoding entirely and emit `{ file, text: '' }`. The mode
96
+ * for BINARY files (PDF, XLSX, PFX/DER): the host gets the `File` intact
97
+ * and `text` carries nothing, so do not read it in this mode.
98
+ */
99
+ readAs?: FileUploadReadAs;
100
+ /** Override the fixed English copy. Omitted fields keep their defaults. */
101
+ labels?: FileUploadLabels;
102
+ /** Fires on accept (with the result) or clear (null). See `readAs` for whether `text` is populated. */
53
103
  onSelect: (result: FileUploadResult | null) => void;
54
104
  /** Fires when a pick is rejected (size/type/read). Optional — the component also shows its own inline error. */
55
105
  onError?: (error: FileUploadError) => void;
@@ -60,7 +110,7 @@ export type FileUploadProps = {
60
110
  'aria-required'?: boolean;
61
111
  'aria-describedby'?: string;
62
112
  'aria-label'?: string;
63
- } & Omit<React.InputHTMLAttributes<HTMLInputElement>, 'type' | 'accept' | 'value' | 'disabled' | 'onChange' | 'onSelect' | 'className' | 'multiple'>;
113
+ } & Omit<React.InputHTMLAttributes<HTMLInputElement>, 'type' | 'accept' | 'value' | 'disabled' | 'onChange' | 'onSelect' | 'className' | 'onError' | 'multiple'>;
64
114
  /**
65
115
  * Validate a chosen file against an accept filter and a byte ceiling. Exported
66
116
  * so hosts can pre-validate before handing a file over. `accept` is matched
@@ -72,6 +122,12 @@ export declare function validateFile(file: File, opts: {
72
122
  accept?: string;
73
123
  maxSizeBytes?: number;
74
124
  }): FileUploadError | null;
125
+ /**
126
+ * Humanize a byte count for the selected-file chip. Binary units, 1 decimal.
127
+ * Exported so a `labels.error` override can format `maxSizeBytes` exactly the
128
+ * way the chip and the default message do, instead of re-deriving binary units.
129
+ */
130
+ export declare function humanizeSize(bytes: number): string;
75
131
  export declare const FileUpload: React.ForwardRefExoticComponent<{
76
132
  /** Comma-separated accept filter, e.g. ".pem,.key" or "text/plain". Mirrors the native input accept. */
77
133
  accept?: string;
@@ -79,7 +135,18 @@ export declare const FileUpload: React.ForwardRefExoticComponent<{
79
135
  maxSizeBytes?: number;
80
136
  /** Controlled selection. `null` = empty. The host owns state. */
81
137
  value?: FileUploadResult | null;
82
- /** Fires on accept (with {file,text}) or clear (null). Reads UTF-8 via FileReader.readAsText. */
138
+ /**
139
+ * How the accepted file is handed over.
140
+ * - `'text'` (default): decode as UTF-8 via FileReader.readAsText and emit
141
+ * `{ file, text }`. Unchanged behaviour for every existing consumer.
142
+ * - `'none'`: skip decoding entirely and emit `{ file, text: '' }`. The mode
143
+ * for BINARY files (PDF, XLSX, PFX/DER): the host gets the `File` intact
144
+ * and `text` carries nothing, so do not read it in this mode.
145
+ */
146
+ readAs?: FileUploadReadAs;
147
+ /** Override the fixed English copy. Omitted fields keep their defaults. */
148
+ labels?: FileUploadLabels;
149
+ /** Fires on accept (with the result) or clear (null). See `readAs` for whether `text` is populated. */
83
150
  onSelect: (result: FileUploadResult | null) => void;
84
151
  /** Fires when a pick is rejected (size/type/read). Optional — the component also shows its own inline error. */
85
152
  onError?: (error: FileUploadError) => void;
@@ -90,5 +157,5 @@ export declare const FileUpload: React.ForwardRefExoticComponent<{
90
157
  'aria-required'?: boolean;
91
158
  'aria-describedby'?: string;
92
159
  'aria-label'?: string;
93
- } & Omit<React.InputHTMLAttributes<HTMLInputElement>, "className" | "disabled" | "type" | "value" | "onChange" | "onSelect" | "accept" | "multiple"> & React.RefAttributes<HTMLInputElement>>;
160
+ } & Omit<React.InputHTMLAttributes<HTMLInputElement>, "className" | "disabled" | "type" | "value" | "onChange" | "onError" | "onSelect" | "accept" | "multiple"> & React.RefAttributes<HTMLInputElement>>;
94
161
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/components/ui/file-upload/index.tsx"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAM9B,MAAM,MAAM,gBAAgB,GAAG;IAAE,IAAI,EAAE,IAAI,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAA;AAE3D,MAAM,MAAM,eAAe,GACvB;IAAE,IAAI,EAAE,WAAW,CAAC;IAAC,IAAI,EAAE,IAAI,CAAC;IAAC,YAAY,EAAE,MAAM,CAAA;CAAE,GACvD;IAAE,IAAI,EAAE,YAAY,CAAC;IAAC,IAAI,EAAE,IAAI,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GAClD;IAAE,IAAI,EAAE,aAAa,CAAC;IAAC,IAAI,EAAE,IAAI,CAAA;CAAE,CAAA;AAEvC,MAAM,MAAM,eAAe,GAAG;IAC5B,wGAAwG;IACxG,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,0FAA0F;IAC1F,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,iEAAiE;IACjE,KAAK,CAAC,EAAE,gBAAgB,GAAG,IAAI,CAAA;IAC/B,iGAAiG;IACjG,QAAQ,EAAE,CAAC,MAAM,EAAE,gBAAgB,GAAG,IAAI,KAAK,IAAI,CAAA;IACnD,gHAAgH;IAChH,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,eAAe,KAAK,IAAI,CAAA;IAC1C,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,EAAE,CAAC,EAAE,MAAM,CAAA;IACX,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,cAAc,CAAC,EAAE,OAAO,CAAA;IACxB,eAAe,CAAC,EAAE,OAAO,CAAA;IACzB,kBAAkB,CAAC,EAAE,MAAM,CAAA;IAC3B,YAAY,CAAC,EAAE,MAAM,CAAA;CACtB,GAAG,IAAI,CACN,KAAK,CAAC,mBAAmB,CAAC,gBAAgB,CAAC,EAEzC,MAAM,GACN,QAAQ,GACR,OAAO,GACP,UAAU,GACV,UAAU,GACV,UAAU,GACV,WAAW,GAEX,UAAU,CACb,CAAA;AAED;;;;;;GAMG;AACH,wBAAgB,YAAY,CAC1B,IAAI,EAAE,IAAI,EACV,IAAI,EAAE;IAAE,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,YAAY,CAAC,EAAE,MAAM,CAAA;CAAE,GAC/C,eAAe,GAAG,IAAI,CASxB;AA8CD,eAAO,MAAM,UAAU;IAhGrB,wGAAwG;aAC/F,MAAM;IACf,0FAA0F;mBAC3E,MAAM;IACrB,iEAAiE;YACzD,gBAAgB,GAAG,IAAI;IAC/B,iGAAiG;cACvF,CAAC,MAAM,EAAE,gBAAgB,GAAG,IAAI,KAAK,IAAI;IACnD,gHAAgH;cACtG,CAAC,KAAK,EAAE,eAAe,KAAK,IAAI;eAC/B,OAAO;SACb,MAAM;gBACC,MAAM;qBACD,OAAO;sBACN,OAAO;yBACJ,MAAM;mBACZ,MAAM;6LA6RtB,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/components/ui/file-upload/index.tsx"],"names":[],"mappings":"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"}
@@ -36,13 +36,21 @@ var __importStar = (this && this.__importStar) || (function () {
36
36
  Object.defineProperty(exports, "__esModule", { value: true });
37
37
  exports.FileUpload = void 0;
38
38
  exports.validateFile = validateFile;
39
+ exports.humanizeSize = humanizeSize;
39
40
  const jsx_runtime_1 = require("react/jsx-runtime");
40
41
  /**
41
- * FileUpload — a controlled "pick a file, read it to text, hand back the text"
42
- * primitive. The defining job: select one file (by click, keyboard, or
43
- * drag-and-drop), validate it against an accept filter + a byte ceiling, read
44
- * it as UTF-8 via FileReader.readAsText, and emit `{ file, text }`. Binary/DER
45
- * (.pfx/.p12) is explicitly OUT of scope — readAsText only.
42
+ * FileUpload — a controlled "pick a file, hand it back" primitive. The defining
43
+ * job: select one file (by click, keyboard, or drag-and-drop), validate it
44
+ * against an accept filter + a byte ceiling, and emit `{ file, text }`.
45
+ *
46
+ * `readAs` decides whether the bytes are decoded on the way through. `'text'`
47
+ * (the default) reads UTF-8 via FileReader.readAsText, which is what a PEM/CSV
48
+ * host wants. `'none'` hands the `File` over untouched and is the only mode a
49
+ * BINARY file can use: a PDF, XLSX or PFX put through readAsText decodes into
50
+ * replacement-character garbage, and that garbage string is then retained for
51
+ * as long as the host holds the value — 20 MiB of PDF became a useless 20 MiB
52
+ * string, and the host still had to read the file a second time to get at the
53
+ * real bytes.
46
54
  *
47
55
  * The real `<input type="file">` IS the accessible control: it is `sr-only`
48
56
  * (visually hidden) but focusable and labelable — never `aria-hidden`, never
@@ -63,6 +71,11 @@ const jsx_runtime_1 = require("react/jsx-runtime");
63
71
  * a value, so it cannot masquerade as valid. `accept` is validated against
64
72
  * BOTH extension and MIME because the native `accept` attribute is only a
65
73
  * browser hint and is bypassable via drag-drop.
74
+ *
75
+ * A host that already announces the rejection itself — a toast in its own
76
+ * locale, driven off `onError` — silences this one by returning nothing from
77
+ * `labels.error`. Two announcements for one event, in two languages, is the
78
+ * accessibility defect; the wording is not.
66
79
  */
67
80
  const React = __importStar(require("react"));
68
81
  const lucide_react_1 = require("lucide-react");
@@ -105,7 +118,11 @@ function matchesAccept(file, accept) {
105
118
  return mime === token;
106
119
  });
107
120
  }
108
- /** Humanize a byte count for the selected-file chip. Binary units, 1 decimal. */
121
+ /**
122
+ * Humanize a byte count for the selected-file chip. Binary units, 1 decimal.
123
+ * Exported so a `labels.error` override can format `maxSizeBytes` exactly the
124
+ * way the chip and the default message do, instead of re-deriving binary units.
125
+ */
109
126
  function humanizeSize(bytes) {
110
127
  if (bytes < 1024)
111
128
  return `${bytes} B`;
@@ -118,8 +135,8 @@ function humanizeSize(bytes) {
118
135
  }
119
136
  return `${size.toFixed(1)} ${units[unit]}`;
120
137
  }
121
- /** A human-readable announcement for each rejection kind. */
122
- function errorMessage(error) {
138
+ /** The default English announcement for each rejection kind. */
139
+ function defaultErrorMessage(error) {
123
140
  switch (error.kind) {
124
141
  case 'too-large':
125
142
  return `File is too large (max ${humanizeSize(error.maxSizeBytes)}).`;
@@ -129,7 +146,7 @@ function errorMessage(error) {
129
146
  return 'Could not read the file.';
130
147
  }
131
148
  }
132
- exports.FileUpload = React.forwardRef(function FileUpload({ accept, maxSizeBytes, value, onSelect, onError, disabled = false, id, className, 'aria-invalid': ariaInvalid, 'aria-required': ariaRequired, 'aria-describedby': ariaDescribedby, 'aria-label': ariaLabel, ...rest }, ref) {
149
+ exports.FileUpload = React.forwardRef(function FileUpload({ accept, maxSizeBytes, value, readAs = 'text', labels, onSelect, onError, disabled = false, id, className, 'aria-invalid': ariaInvalid, 'aria-required': ariaRequired, 'aria-describedby': ariaDescribedby, 'aria-label': ariaLabel, ...rest }, ref) {
133
150
  const internalRef = React.useRef(null);
134
151
  React.useImperativeHandle(ref, () => internalRef.current);
135
152
  const reactId = React.useId();
@@ -141,12 +158,21 @@ exports.FileUpload = React.forwardRef(function FileUpload({ accept, maxSizeBytes
141
158
  // component's own validation/read error — a self-detected bad pick must read
142
159
  // as invalid even outside a form.
143
160
  const invalid = ariaInvalid || error !== null;
161
+ // Resolve the announcement BEFORE deciding whether the alert exists: a
162
+ // consumer that returns nothing is opting out of this surface entirely,
163
+ // and an association pointing at an unrendered node is worse than none.
164
+ const resolved = error
165
+ ? labels?.error
166
+ ? labels.error(error)
167
+ : defaultErrorMessage(error)
168
+ : null;
169
+ const errorText = resolved ? resolved : null;
144
170
  // Radix Slot OVERWRITES aria-describedby (it does not merge), so merge the
145
171
  // primitive's own role=alert error id with the FormControl-injected one so
146
172
  // both associations coexist on the input. A plain join, never `cn` —
147
173
  // tailwind-merge treats these as class names and would drop an id that
148
174
  // happens to look like a conflicting utility.
149
- const describedBy = [ariaDescribedby, error ? errorId : undefined]
175
+ const describedBy = [ariaDescribedby, errorText ? errorId : undefined]
150
176
  .filter(Boolean)
151
177
  .join(' ') || undefined;
152
178
  // Last-resolved-wins: a slow read for pick A must not overwrite a newer
@@ -162,6 +188,15 @@ exports.FileUpload = React.forwardRef(function FileUpload({ accept, maxSizeBytes
162
188
  onError?.(validationError);
163
189
  return;
164
190
  }
191
+ // Binary path: no decode, no reader, no retained garbage string. The
192
+ // stale-rejection clear still has to happen here — the alert is this
193
+ // component's own state and a good pick must retire it.
194
+ if (readAs === 'none') {
195
+ readerRef.current = null;
196
+ setError(null);
197
+ onSelect({ file, text: '' });
198
+ return;
199
+ }
165
200
  const reader = new FileReader();
166
201
  readerRef.current = reader;
167
202
  reader.onload = () => {
@@ -228,10 +263,10 @@ exports.FileUpload = React.forwardRef(function FileUpload({ accept, maxSizeBytes
228
263
  return ((0, jsx_runtime_1.jsxs)("div", { className: (0, utils_1.cn)('space-y-2', className), children: [(0, jsx_runtime_1.jsxs)("div", { onClick: openPicker, onDragOver: onDragOver, onDragLeave: onDragLeave, onDrop: onDrop, className: (0, utils_1.cn)('border-input bg-card focus-within:ring-ring focus-within:ring-offset-background aria-[invalid=true]:border-destructive aria-[invalid=true]:focus-within:ring-destructive flex w-full items-center gap-3 rounded-md border px-3 py-4 text-sm shadow-xs transition-colors focus-within:ring-2 focus-within:ring-offset-1 focus-within:outline-none', dragActive &&
229
264
  'border-ring ring-ring ring-offset-background ring-2 ring-offset-1', disabled
230
265
  ? 'border-muted bg-muted/30 cursor-not-allowed shadow-none'
231
- : 'cursor-pointer'), "aria-invalid": invalid || undefined, children: [(0, jsx_runtime_1.jsx)("input", { ref: internalRef, id: inputId, type: "file", accept: accept, disabled: disabled, className: "sr-only", "aria-invalid": invalid || undefined, "aria-required": ariaRequired || undefined, "aria-describedby": describedBy, "aria-label": ariaLabel, onChange: onInputChange, ...rest }), (0, jsx_runtime_1.jsx)(lucide_react_1.Upload, { className: "text-muted-foreground size-4 shrink-0", "aria-hidden": "true" }), value ? ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsxs)("span", { className: "min-w-0 flex-1 truncate", children: [(0, jsx_runtime_1.jsx)("span", { className: "text-foreground font-medium", children: value.file.name }), ' ', (0, jsx_runtime_1.jsx)("span", { className: "text-muted-foreground tabular-nums", children: humanizeSize(value.file.size) })] }), (0, jsx_runtime_1.jsx)(icon_button_1.IconButton, { type: "button", variant: "plain", size: "small", disabled: disabled, "aria-label": "Remove file", onClick: (event) => {
266
+ : 'cursor-pointer'), "aria-invalid": invalid || undefined, children: [(0, jsx_runtime_1.jsx)("input", { ref: internalRef, id: inputId, type: "file", accept: accept, disabled: disabled, className: "sr-only", "aria-invalid": invalid || undefined, "aria-required": ariaRequired || undefined, "aria-describedby": describedBy, "aria-label": ariaLabel, onChange: onInputChange, ...rest }), (0, jsx_runtime_1.jsx)(lucide_react_1.Upload, { className: "text-muted-foreground size-4 shrink-0", "aria-hidden": "true" }), value ? ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsxs)("span", { className: "min-w-0 flex-1 truncate", children: [(0, jsx_runtime_1.jsx)("span", { className: "text-foreground font-medium", children: value.file.name }), ' ', (0, jsx_runtime_1.jsx)("span", { className: "text-muted-foreground tabular-nums", children: humanizeSize(value.file.size) })] }), (0, jsx_runtime_1.jsx)(icon_button_1.IconButton, { type: "button", variant: "plain", size: "small", disabled: disabled, "aria-label": labels?.remove?.trim() ? labels.remove : 'Remove file', onClick: (event) => {
232
267
  // Don't bubble to the zone's openPicker and re-open the dialog.
233
268
  event.stopPropagation();
234
269
  clear();
235
- }, children: (0, jsx_runtime_1.jsx)(lucide_react_1.X, { className: "size-4", "aria-hidden": "true" }) })] })) : ((0, jsx_runtime_1.jsxs)("span", { className: "text-muted-foreground", children: [(0, jsx_runtime_1.jsx)("span", { className: "text-foreground font-medium", children: "Choose a file" }), ' ', "or drag and drop"] }))] }), error ? ((0, jsx_runtime_1.jsx)("p", { id: errorId, role: "alert", className: "text-destructive text-xs font-medium", children: errorMessage(error) })) : null] }));
270
+ }, children: (0, jsx_runtime_1.jsx)(lucide_react_1.X, { className: "size-4", "aria-hidden": "true" }) })] })) : ((0, jsx_runtime_1.jsxs)("span", { className: "text-muted-foreground", children: [(0, jsx_runtime_1.jsx)("span", { className: "text-foreground font-medium", children: labels?.action ?? 'Choose a file' }), ' ', labels?.hint ?? 'or drag and drop'] }))] }), errorText ? ((0, jsx_runtime_1.jsx)("p", { id: errorId, role: "alert", className: "text-system-error-h1a text-xs font-medium", children: errorText })) : null] }));
236
271
  });
237
272
  exports.FileUpload.displayName = 'FileUpload';