@lerianstudio/sindarian-ui 2.0.0-beta.1 → 2.0.0-beta.10

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 (136) hide show
  1. package/dist/components/breadcrumb/index.d.ts.map +1 -1
  2. package/dist/components/breadcrumb/index.js +25 -1
  3. package/dist/components/card/account-card/index.js +5 -5
  4. package/dist/components/card/basic-card/index.d.ts.map +1 -1
  5. package/dist/components/card/basic-card/index.js +2 -2
  6. package/dist/components/card/entity-card/index.js +1 -1
  7. package/dist/components/dialog/confirmation-dialog/index.d.ts.map +1 -1
  8. package/dist/components/dialog/confirmation-dialog/index.js +4 -1
  9. package/dist/components/entity-box/index.d.ts +18 -2
  10. package/dist/components/entity-box/index.d.ts.map +1 -1
  11. package/dist/components/entity-box/index.js +32 -4
  12. package/dist/components/form/file-upload-field/index.d.ts +11 -2
  13. package/dist/components/form/file-upload-field/index.d.ts.map +1 -1
  14. package/dist/components/form/file-upload-field/index.js +8 -3
  15. package/dist/components/form/input-field/index.d.ts +41 -2
  16. package/dist/components/form/input-field/index.d.ts.map +1 -1
  17. package/dist/components/form/input-field/index.js +73 -15
  18. package/dist/components/form/password-field/index.d.ts +5 -1
  19. package/dist/components/form/password-field/index.d.ts.map +1 -1
  20. package/dist/components/form/password-field/index.js +2 -2
  21. package/dist/components/form/select-field/index.d.ts +9 -1
  22. package/dist/components/form/select-field/index.d.ts.map +1 -1
  23. package/dist/components/form/select-field/index.js +2 -2
  24. package/dist/components/page-header/index.js +1 -1
  25. package/dist/components/table/id-table-cell.d.ts +5 -1
  26. package/dist/components/table/id-table-cell.d.ts.map +1 -1
  27. package/dist/components/table/id-table-cell.js +48 -6
  28. package/dist/components/ui/autocomplete/index.d.ts +24 -0
  29. package/dist/components/ui/autocomplete/index.d.ts.map +1 -1
  30. package/dist/components/ui/autocomplete/index.js +24 -0
  31. package/dist/components/ui/badge/index.d.ts +11 -7
  32. package/dist/components/ui/badge/index.d.ts.map +1 -1
  33. package/dist/components/ui/badge/index.js +20 -8
  34. package/dist/components/ui/button/styles.css +31 -1
  35. package/dist/components/ui/calendar/index.d.ts.map +1 -1
  36. package/dist/components/ui/calendar/index.js +7 -1
  37. package/dist/components/ui/copy-field/index.d.ts.map +1 -1
  38. package/dist/components/ui/copy-field/index.js +1 -1
  39. package/dist/components/ui/file-upload/index.d.ts +235 -9
  40. package/dist/components/ui/file-upload/index.d.ts.map +1 -1
  41. package/dist/components/ui/file-upload/index.js +354 -17
  42. package/dist/components/ui/form.d.ts +4 -0
  43. package/dist/components/ui/form.d.ts.map +1 -1
  44. package/dist/components/ui/form.js +48 -6
  45. package/dist/components/ui/input/styles.css +30 -2
  46. package/dist/components/ui/sheet/index.d.ts.map +1 -1
  47. package/dist/components/ui/sheet/index.js +2 -2
  48. package/dist/components/ui/sidebar/index.d.ts +1 -0
  49. package/dist/components/ui/sidebar/index.d.ts.map +1 -1
  50. package/dist/components/ui/sidebar/index.js +1 -0
  51. package/dist/components/ui/sidebar/sidebar-expand-button.d.ts +1 -1
  52. package/dist/components/ui/sidebar/sidebar-expand-button.d.ts.map +1 -1
  53. package/dist/components/ui/sidebar/sidebar-expand-button.js +15 -1
  54. package/dist/components/ui/sidebar/sidebar-item.d.ts +1 -1
  55. package/dist/components/ui/sidebar/sidebar-item.d.ts.map +1 -1
  56. package/dist/components/ui/sidebar/sidebar-item.js +28 -4
  57. package/dist/components/ui/sidebar/sidebar-provider.d.ts +37 -0
  58. package/dist/components/ui/sidebar/sidebar-provider.d.ts.map +1 -1
  59. package/dist/components/ui/sidebar/sidebar-provider.js +125 -4
  60. package/dist/components/ui/sidebar/sidebar-root.d.ts +38 -1
  61. package/dist/components/ui/sidebar/sidebar-root.d.ts.map +1 -1
  62. package/dist/components/ui/sidebar/sidebar-root.js +100 -5
  63. package/dist/components/ui/sidebar/sidebar-trigger.d.ts +18 -0
  64. package/dist/components/ui/sidebar/sidebar-trigger.d.ts.map +1 -0
  65. package/dist/components/ui/sidebar/sidebar-trigger.js +30 -0
  66. package/dist/components/ui/toggle-group/index.d.ts +1 -1
  67. package/dist/components/ui/toggle-group/index.d.ts.map +1 -1
  68. package/dist/components/ui/toggle-group/index.js +73 -4
  69. package/dist/esm/components/breadcrumb/index.d.ts.map +1 -1
  70. package/dist/esm/components/breadcrumb/index.js +25 -1
  71. package/dist/esm/components/card/account-card/index.js +5 -5
  72. package/dist/esm/components/card/basic-card/index.d.ts.map +1 -1
  73. package/dist/esm/components/card/basic-card/index.js +2 -2
  74. package/dist/esm/components/card/entity-card/index.js +1 -1
  75. package/dist/esm/components/dialog/confirmation-dialog/index.d.ts.map +1 -1
  76. package/dist/esm/components/dialog/confirmation-dialog/index.js +4 -1
  77. package/dist/esm/components/entity-box/index.d.ts +18 -2
  78. package/dist/esm/components/entity-box/index.d.ts.map +1 -1
  79. package/dist/esm/components/entity-box/index.js +29 -4
  80. package/dist/esm/components/form/file-upload-field/index.d.ts +11 -2
  81. package/dist/esm/components/form/file-upload-field/index.d.ts.map +1 -1
  82. package/dist/esm/components/form/file-upload-field/index.js +8 -3
  83. package/dist/esm/components/form/input-field/index.d.ts +41 -2
  84. package/dist/esm/components/form/input-field/index.d.ts.map +1 -1
  85. package/dist/esm/components/form/input-field/index.js +73 -15
  86. package/dist/esm/components/form/password-field/index.d.ts +5 -1
  87. package/dist/esm/components/form/password-field/index.d.ts.map +1 -1
  88. package/dist/esm/components/form/password-field/index.js +2 -2
  89. package/dist/esm/components/form/select-field/index.d.ts +9 -1
  90. package/dist/esm/components/form/select-field/index.d.ts.map +1 -1
  91. package/dist/esm/components/form/select-field/index.js +2 -2
  92. package/dist/esm/components/page-header/index.js +1 -1
  93. package/dist/esm/components/table/id-table-cell.d.ts +5 -1
  94. package/dist/esm/components/table/id-table-cell.d.ts.map +1 -1
  95. package/dist/esm/components/table/id-table-cell.js +48 -3
  96. package/dist/esm/components/ui/autocomplete/index.d.ts +24 -0
  97. package/dist/esm/components/ui/autocomplete/index.d.ts.map +1 -1
  98. package/dist/esm/components/ui/autocomplete/index.js +24 -0
  99. package/dist/esm/components/ui/badge/index.d.ts +11 -7
  100. package/dist/esm/components/ui/badge/index.d.ts.map +1 -1
  101. package/dist/esm/components/ui/badge/index.js +20 -8
  102. package/dist/esm/components/ui/calendar/index.d.ts.map +1 -1
  103. package/dist/esm/components/ui/calendar/index.js +8 -2
  104. package/dist/esm/components/ui/copy-field/index.d.ts.map +1 -1
  105. package/dist/esm/components/ui/copy-field/index.js +1 -1
  106. package/dist/esm/components/ui/file-upload/index.d.ts +235 -9
  107. package/dist/esm/components/ui/file-upload/index.d.ts.map +1 -1
  108. package/dist/esm/components/ui/file-upload/index.js +353 -17
  109. package/dist/esm/components/ui/form.d.ts +4 -0
  110. package/dist/esm/components/ui/form.d.ts.map +1 -1
  111. package/dist/esm/components/ui/form.js +48 -6
  112. package/dist/esm/components/ui/sheet/index.d.ts.map +1 -1
  113. package/dist/esm/components/ui/sheet/index.js +2 -2
  114. package/dist/esm/components/ui/sidebar/index.d.ts +1 -0
  115. package/dist/esm/components/ui/sidebar/index.d.ts.map +1 -1
  116. package/dist/esm/components/ui/sidebar/index.js +1 -0
  117. package/dist/esm/components/ui/sidebar/sidebar-expand-button.d.ts +1 -1
  118. package/dist/esm/components/ui/sidebar/sidebar-expand-button.d.ts.map +1 -1
  119. package/dist/esm/components/ui/sidebar/sidebar-expand-button.js +15 -1
  120. package/dist/esm/components/ui/sidebar/sidebar-item.d.ts +1 -1
  121. package/dist/esm/components/ui/sidebar/sidebar-item.d.ts.map +1 -1
  122. package/dist/esm/components/ui/sidebar/sidebar-item.js +28 -4
  123. package/dist/esm/components/ui/sidebar/sidebar-provider.d.ts +37 -0
  124. package/dist/esm/components/ui/sidebar/sidebar-provider.d.ts.map +1 -1
  125. package/dist/esm/components/ui/sidebar/sidebar-provider.js +122 -1
  126. package/dist/esm/components/ui/sidebar/sidebar-root.d.ts +38 -1
  127. package/dist/esm/components/ui/sidebar/sidebar-root.d.ts.map +1 -1
  128. package/dist/esm/components/ui/sidebar/sidebar-root.js +102 -7
  129. package/dist/esm/components/ui/sidebar/sidebar-trigger.d.ts +18 -0
  130. package/dist/esm/components/ui/sidebar/sidebar-trigger.d.ts.map +1 -0
  131. package/dist/esm/components/ui/sidebar/sidebar-trigger.js +26 -0
  132. package/dist/esm/components/ui/toggle-group/index.d.ts +1 -1
  133. package/dist/esm/components/ui/toggle-group/index.d.ts.map +1 -1
  134. package/dist/esm/components/ui/toggle-group/index.js +73 -4
  135. package/dist/globals.css +77 -31
  136. package/package.json +5 -3
@@ -63,12 +63,58 @@ const toggleVariants = (0, class_variance_authority_1.cva)("hover:bg-muted hover
63
63
  size: 'default'
64
64
  }
65
65
  });
66
+ const ARROW_KEYS = ['ArrowUp', 'ArrowDown', 'ArrowLeft', 'ArrowRight'];
67
+ /**
68
+ * Is an arrow key down right now?
69
+ *
70
+ * This is how an item tells keyboard navigation apart from a pointer press or a
71
+ * programmatic focus, and it is the seam Radix uses for the same job in its own
72
+ * `RadioGroup`. It works because `RovingFocusGroup` defers the focus move to a
73
+ * `setTimeout`: the keydown has already bubbled to `document` and set the flag
74
+ * by the time the item receives `focus`.
75
+ *
76
+ * The distinction is not cosmetic. A pointer press focuses BEFORE the click
77
+ * lands, so selecting on every focus would turn one click into
78
+ * select-then-deselect and break mouse selection outright.
79
+ *
80
+ * Returns null when disabled, so `type="multiple"` carries no flag to read.
81
+ */
82
+ function useArrowKeyPressed(enabled) {
83
+ const arrowKeyPressed = React.useRef(false);
84
+ React.useEffect(() => {
85
+ if (!enabled)
86
+ return;
87
+ const onKeyDown = (event) => {
88
+ if (ARROW_KEYS.includes(event.key))
89
+ arrowKeyPressed.current = true;
90
+ };
91
+ const onKeyUp = () => {
92
+ arrowKeyPressed.current = false;
93
+ };
94
+ document.addEventListener('keydown', onKeyDown);
95
+ document.addEventListener('keyup', onKeyUp);
96
+ window.addEventListener('blur', onKeyUp);
97
+ return () => {
98
+ document.removeEventListener('keydown', onKeyDown);
99
+ document.removeEventListener('keyup', onKeyUp);
100
+ window.removeEventListener('blur', onKeyUp);
101
+ // A group unmounted mid-keystroke never sees the keyup.
102
+ arrowKeyPressed.current = false;
103
+ };
104
+ }, [enabled]);
105
+ return enabled ? arrowKeyPressed : null;
106
+ }
66
107
  const ToggleGroupContext = React.createContext({
67
108
  size: 'default',
68
109
  variant: 'default',
69
- spacing: 0
110
+ spacing: 0,
111
+ arrowKeyPressed: null
70
112
  });
71
113
  function ToggleGroup({ className, variant, size, spacing = 0, children, style, ...props }) {
114
+ // Read, never destructured: `type` is the discriminant of the Radix props
115
+ // union, and pulling it out then spreading it back widens the union so the
116
+ // single/multiple props stop resolving.
117
+ const arrowKeyPressed = useArrowKeyPressed(props.type === 'single');
72
118
  return ((0, jsx_runtime_1.jsx)(ToggleGroupPrimitive.Root, { "data-slot": "toggle-group", "data-variant": variant, "data-size": size, "data-spacing": spacing,
73
119
  // Caller style first: `--gap` is ours and must survive, but everything
74
120
  // else the caller passes has to reach the element.
@@ -76,12 +122,35 @@ function ToggleGroup({ className, variant, size, spacing = 0, children, style, .
76
122
  // Legacy carried a `data-[spacing=default]:...:shadow-xs` rule here that
77
123
  // never matched — `data-spacing` is always a number. Removed rather than
78
124
  // activated, so the rendered pixels stay identical to sindarian-x.
79
- 'group/toggle-group flex w-fit items-center gap-[--spacing(var(--gap))] rounded-md', className), ...props, children: (0, jsx_runtime_1.jsx)(ToggleGroupContext.Provider, { value: { variant, size, spacing }, children: children }) }));
125
+ 'group/toggle-group flex w-fit items-center gap-[--spacing(var(--gap))] rounded-md', className), ...props, children: (0, jsx_runtime_1.jsx)(ToggleGroupContext.Provider, { value: { variant, size, spacing, arrowKeyPressed }, children: children }) }));
80
126
  }
81
- function ToggleGroupItem({ className, children, variant, size, ...props }) {
127
+ function ToggleGroupItem({ className, children, variant, size, onFocus, ...props }) {
82
128
  const context = React.useContext(ToggleGroupContext);
129
+ /**
130
+ * Selection follows focus, which is what `role="radio"` promises.
131
+ *
132
+ * With `type="single"` Radix puts the group on the ARIA radio pattern: the
133
+ * root is a `radiogroup`, each item a `radio` with `aria-checked`. Under that
134
+ * pattern an arrow key both moves focus and checks the item it lands on.
135
+ * Radix only moves focus, so a screen-reader operator used to arrow across
136
+ * "radio, not checked, 2 of 3" with nothing ever selected.
137
+ *
138
+ * `type="multiple"` is a `toolbar` of `aria-pressed` buttons and keeps its
139
+ * current behavior, where moving focus without pressing is correct: the flag
140
+ * is null there.
141
+ *
142
+ * Clicking the element rather than writing the value keeps a single
143
+ * activation path, so Radix's own toggle and any caller `onClick` both fire
144
+ * exactly as they do for a real click.
145
+ */
146
+ const handleFocus = (event) => {
147
+ onFocus?.(event);
148
+ if (context.arrowKeyPressed?.current) {
149
+ event.currentTarget.click();
150
+ }
151
+ };
83
152
  return ((0, jsx_runtime_1.jsx)(ToggleGroupPrimitive.Item, { "data-slot": "toggle-group-item", "data-variant": context.variant || variant, "data-size": context.size || size, "data-spacing": context.spacing, className: (0, utils_1.cn)(toggleVariants({
84
153
  variant: context.variant || variant,
85
154
  size: context.size || size
86
- }), 'w-auto min-w-0 shrink-0 px-3 focus:z-10 focus-visible:z-10', 'data-[spacing=0]:rounded-none data-[spacing=0]:shadow-none data-[spacing=0]:first:rounded-l-md data-[spacing=0]:last:rounded-r-md data-[spacing=0]:data-[variant=outline]:border-l-0 data-[spacing=0]:data-[variant=outline]:first:border-l', className), ...props, children: children }));
155
+ }), 'w-auto min-w-0 shrink-0 px-3 focus:z-10 focus-visible:z-10', 'data-[spacing=0]:rounded-none data-[spacing=0]:shadow-none data-[spacing=0]:first:rounded-l-md data-[spacing=0]:last:rounded-r-md data-[spacing=0]:data-[variant=outline]:border-l-0 data-[spacing=0]:data-[variant=outline]:first:border-l', className), ...props, onFocus: handleFocus, children: children }));
87
156
  }
@@ -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
  };
@@ -23,7 +23,7 @@ export function AccountBalanceCardHeader({ className, ...props }) {
23
23
  return (_jsx("div", { "data-slot": "account-balance-card-header", className: cn('flex flex-col', className), ...props }));
24
24
  }
25
25
  export function AccountBalanceCardTitle({ className, ...props }) {
26
- return (_jsx("p", { "data-slot": "account-balance-card-title", className: cn('text-shadcn-600 text-lg font-bold', className), ...props }));
26
+ return (_jsx("p", { "data-slot": "account-balance-card-title", className: cn('text-foreground text-lg font-bold', className), ...props }));
27
27
  }
28
28
  export function AccountBalanceCardDeleteButton({ className, ...props }) {
29
29
  return (_jsx(IconButton, { variant: "outline", size: "small", rounded: true, "data-slot": "account-balance-card-delete-button", className: cn('absolute top-3 right-3', className), ...props, children: _jsx(Trash, { className: "text-muted-foreground h-4 w-4" }) }));
@@ -35,19 +35,19 @@ export function AccountBalanceCardLoading({ className, ...props }) {
35
35
  return (_jsx(Skeleton, { "data-slot": "account-balance-card-loading", className: cn('bg-muted mt-3 h-3 w-full rounded-md', className), ...props }));
36
36
  }
37
37
  export function AccountBalanceCardEmpty({ className, ...props }) {
38
- return (_jsx("p", { "data-slot": "account-balance-card-empty", className: cn('text-shadcn-500 mt-3 text-center text-sm font-normal', className), ...props }));
38
+ return (_jsx("p", { "data-slot": "account-balance-card-empty", className: cn('text-muted-foreground mt-3 text-center text-sm font-normal', className), ...props }));
39
39
  }
40
40
  export function AccountBalanceCardInfo({ className, assetCode, value, children: _children, ref, ...props }) {
41
- return (_jsxs("div", { ref: ref, "data-slot": "account-balance-card-info", className: cn('text-shadcn-500 mt-3 flex flex-row items-center justify-between text-sm font-normal', className), ...props, children: [_jsx("p", { children: assetCode }), _jsx("p", { children: value })] }));
41
+ return (_jsxs("div", { ref: ref, "data-slot": "account-balance-card-info", className: cn('text-muted-foreground mt-3 flex flex-row items-center justify-between text-sm font-normal', className), ...props, children: [_jsx("p", { children: assetCode }), _jsx("p", { children: value })] }));
42
42
  }
43
43
  export function AccountBalanceCardUpdateButton({ className: _className, loading, timestamp, loadingLabel = 'Updating...', updatedInLabel = 'Updated in', updatedLabel = 'Updated', onRefresh, children: _children, ref, ...props }) {
44
44
  const _time = useTime({ interval: 1000 * 60 });
45
45
  const updated = React.useMemo(() => {
46
46
  return !dayjs(timestamp).isBefore(dayjs().subtract(1, 'minute'));
47
47
  }, [_time, timestamp]);
48
- return (_jsxs("div", { className: "mb-3 flex flex-row items-center justify-end gap-2", children: [_jsxs("p", { className: "text-shadcn-500 text-xs font-medium", children: [loading && loadingLabel, !loading && !updated && updatedInLabel, !loading && updated && updatedLabel] }), !loading && updated && (_jsx(CheckCircle2, { className: "text-system-success-h1a h-4 w-4" })), (!updated || loading) && (_jsx(Button, { ref: ref, variant: "link", "data-slot": "account-balance-card-update-button", className: "h-3 p-0", onClick: onRefresh, disabled: loading, ...props, children: _jsx(RefreshCw, { className: cn('size-4', { 'motion-safe:animate-spin': loading }) }) }))] }));
48
+ return (_jsxs("div", { className: "mb-3 flex flex-row items-center justify-end gap-2", children: [_jsxs("p", { className: "text-muted-foreground text-xs font-medium", children: [loading && loadingLabel, !loading && !updated && updatedInLabel, !loading && updated && updatedLabel] }), !loading && updated && (_jsx(CheckCircle2, { className: "text-system-success-h1a h-4 w-4" })), (!updated || loading) && (_jsx(Button, { ref: ref, variant: "link", "data-slot": "account-balance-card-update-button", className: "h-3 p-0", onClick: onRefresh, disabled: loading, ...props, children: _jsx(RefreshCw, { className: cn('size-4', { 'motion-safe:animate-spin': loading }) }) }))] }));
49
49
  }
50
50
  export function AccountBalanceCardTrigger({ className, openLabel, closeLabel, children: _children, ...props }) {
51
51
  const { open } = React.useContext(AccountBalanceCardContext);
52
- return (_jsx(CollapsibleTrigger, { asChild: true, children: _jsx(Button, { variant: "link", "data-slot": "account-balance-card-trigger", className: cn('text-shadcn-600/40 h-4 p-0 text-xs', className), ...props, children: !open ? openLabel : closeLabel }) }));
52
+ return (_jsx(CollapsibleTrigger, { asChild: true, children: _jsx(Button, { variant: "link", "data-slot": "account-balance-card-trigger", className: cn('text-muted-foreground h-4 p-0 text-xs', className), ...props, children: !open ? openLabel : closeLabel }) }));
53
53
  }
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/components/card/basic-card/index.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AAGzB,wBAAgB,SAAS,CAAC,EACxB,SAAS,EACT,GAAG,KAAK,EACT,EAAE,KAAK,CAAC,cAAc,CAAC,KAAK,CAAC,qBAO7B;AAED,wBAAgB,cAAc,CAAC,EAC7B,SAAS,EACT,GAAG,KAAK,EACT,EAAE,KAAK,CAAC,cAAc,CAAC,IAAI,CAAC,qBAO5B;AAED,wBAAgB,gBAAgB,CAAC,EAC/B,SAAS,EACT,GAAG,KAAK,EACT,EAAE,KAAK,CAAC,cAAc,CAAC,KAAK,CAAC,qBAO7B;AAED,wBAAgB,eAAe,CAAC,EAC9B,SAAS,EACT,GAAG,KAAK,EACT,EAAE,KAAK,CAAC,cAAc,CAAC,KAAK,CAAC,qBAE7B"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/components/card/basic-card/index.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AAGzB,wBAAgB,SAAS,CAAC,EACxB,SAAS,EACT,GAAG,KAAK,EACT,EAAE,KAAK,CAAC,cAAc,CAAC,KAAK,CAAC,qBAO7B;AAED,wBAAgB,cAAc,CAAC,EAC7B,SAAS,EACT,GAAG,KAAK,EACT,EAAE,KAAK,CAAC,cAAc,CAAC,IAAI,CAAC,qBAO5B;AAED,wBAAgB,gBAAgB,CAAC,EAC/B,SAAS,EACT,GAAG,KAAK,EACT,EAAE,KAAK,CAAC,cAAc,CAAC,KAAK,CAAC,qBAU7B;AAED,wBAAgB,eAAe,CAAC,EAC9B,SAAS,EACT,GAAG,KAAK,EACT,EAAE,KAAK,CAAC,cAAc,CAAC,KAAK,CAAC,qBAE7B"}
@@ -4,10 +4,10 @@ export function BasicCard({ className, ...props }) {
4
4
  return (_jsx("div", { className: cn('bg-card flex flex-col rounded-lg p-6', className), ...props }));
5
5
  }
6
6
  export function BasicCardTitle({ className, ...props }) {
7
- return (_jsx("h2", { className: cn('text-shadcn-600 mb-4 text-sm font-medium', className), ...props }));
7
+ return (_jsx("h2", { className: cn('text-foreground mb-4 text-sm font-medium', className), ...props }));
8
8
  }
9
9
  export function BasicCardContent({ className, ...props }) {
10
- return (_jsx("div", { className: cn('text-shadcn-500 mb-6 text-sm font-medium', className), ...props }));
10
+ return (_jsx("div", { className: cn('text-muted-foreground mb-6 text-sm font-medium', className), ...props }));
11
11
  }
12
12
  export function BasicCardAction({ className, ...props }) {
13
13
  return _jsx("div", { className: cn('flex flex-col', className), ...props });
@@ -45,7 +45,7 @@ export function EntityCardDescription({ className, ...props }) {
45
45
  return (_jsx("p", { "data-slot": "entity-card-description", className: cn('text-muted-foreground text-xs font-medium', className), ...props }));
46
46
  }
47
47
  export function EntityCardList({ className, ...props }) {
48
- return (_jsx("ul", { "data-slot": "entity-card-list", className: cn('text-shadcn-500 mx-2 list-inside list-disc space-y-1 text-xs font-medium', className), ...props }));
48
+ return (_jsx("ul", { "data-slot": "entity-card-list", className: cn('text-muted-foreground mx-2 list-inside list-disc space-y-1 text-xs font-medium', className), ...props }));
49
49
  }
50
50
  export function EntityCardFooter({ className, children, ...props }) {
51
51
  return (_jsxs("div", { "data-slot": "entity-card-footer", className: cn('text-muted-foreground flex flex-col gap-2 text-xs font-medium', className), ...props, children: [_jsx(Separator, {}), _jsx("div", { className: "[&>svg]:text-muted-foreground flex flex-row items-center gap-2 [&>svg]:size-4 [&>svg]:stroke-[1.5]", children: children })] }));
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/components/dialog/confirmation-dialog/index.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAuD9B,KAAK,6BAA6B,GAAG;IACnC,IAAI,EAAE,OAAO,CAAA;IACb,YAAY,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAA;IACrC,KAAK,CAAC,EAAE,KAAK,CAAC,SAAS,CAAA;IACvB,WAAW,CAAC,EAAE,KAAK,CAAC,SAAS,CAAA;IAC7B,uDAAuD;IACvD,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,OAAO,CAAC,EAAE,SAAS,GAAG,SAAS,GAAG,aAAa,GAAG,SAAS,CAAA;IAC3D;;;;OAIG;IACH,SAAS,CAAC,EAAE,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IACtC,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAA;IACrB,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,WAAW,CAAC,EAAE,MAAM,CAAA;CACrB,CAAA;AAED;;;;;;;;;;;;;;;GAeG;AACH,KAAK,8BAA8B,GAC/B;IACE,OAAO,EAAE,OAAO,CAAA;IAChB;;;;;;OAMG;IACH,YAAY,EAAE,MAAM,CAAA;CACrB,GACD;IACE,OAAO,CAAC,EAAE,SAAS,CAAA;IACnB;yEACqE;IACrE,YAAY,CAAC,EAAE,MAAM,CAAA;CACtB,CAAA;AAEL,MAAM,MAAM,uBAAuB,GAAG,6BAA6B,GACjE,8BAA8B,CAAA;AAEhC,wBAAgB,kBAAkB,CAAC,EACjC,IAAI,EACJ,YAAY,EACZ,KAAY,EACZ,WAAkB,EAClB,OAAmB,EACnB,OAAO,EACP,SAAoB,EACpB,QAAmB,EACnB,YAAY,EACZ,WAAW,EACX,YAAY,EACb,EAAE,uBAAuB,qBA8FzB"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/components/dialog/confirmation-dialog/index.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAuD9B,KAAK,6BAA6B,GAAG;IACnC,IAAI,EAAE,OAAO,CAAA;IACb,YAAY,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAA;IACrC,KAAK,CAAC,EAAE,KAAK,CAAC,SAAS,CAAA;IACvB,WAAW,CAAC,EAAE,KAAK,CAAC,SAAS,CAAA;IAC7B,uDAAuD;IACvD,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,OAAO,CAAC,EAAE,SAAS,GAAG,SAAS,GAAG,aAAa,GAAG,SAAS,CAAA;IAC3D;;;;OAIG;IACH,SAAS,CAAC,EAAE,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IACtC,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAA;IACrB,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,WAAW,CAAC,EAAE,MAAM,CAAA;CACrB,CAAA;AAED;;;;;;;;;;;;;;;GAeG;AACH,KAAK,8BAA8B,GAC/B;IACE,OAAO,EAAE,OAAO,CAAA;IAChB;;;;;;OAMG;IACH,YAAY,EAAE,MAAM,CAAA;CACrB,GACD;IACE,OAAO,CAAC,EAAE,SAAS,CAAA;IACnB;yEACqE;IACrE,YAAY,CAAC,EAAE,MAAM,CAAA;CACtB,CAAA;AAEL,MAAM,MAAM,uBAAuB,GAAG,6BAA6B,GACjE,8BAA8B,CAAA;AAEhC,wBAAgB,kBAAkB,CAAC,EACjC,IAAI,EACJ,YAAY,EACZ,KAAY,EACZ,WAAkB,EAClB,OAAmB,EACnB,OAAO,EACP,SAAoB,EACpB,QAAmB,EACnB,YAAY,EACZ,WAAW,EACX,YAAY,EACb,EAAE,uBAAuB,qBAiGzB"}
@@ -53,5 +53,8 @@ export function ConfirmationDialog({ open, onOpenChange, title = null, descripti
53
53
  if (busy) {
54
54
  event.preventDefault();
55
55
  }
56
- }, children: [_jsxs(AlertDialogHeader, { className: "flex-row items-center", children: [_jsx(ConfirmationDialogIcon, { variant: variant }), _jsxs("div", { className: "flex flex-col gap-4", children: [title == null || title === false || title === true || title === '' ? (_jsx(AlertDialogTitle, { className: "sr-only", children: confirmLabel || 'Confirm' })) : (_jsx(AlertDialogTitle, { children: title })), description == null ? null : (_jsx(AlertDialogDescription, { children: description }))] })] }), pendingLabel ? (_jsx("span", { role: "status", className: "sr-only", children: busy ? pendingLabel : '' })) : null, _jsxs(AlertDialogFooter, { className: "gap-4", children: [_jsx(AlertDialogCancel, { asChild: true, variant: "secondary", children: _jsx(Button, { onClick: onCancel, disabled: busy, variant: "secondary", size: "small", children: cancelLabel ?? 'Cancel' }) }), _jsx(LoadingButton, { loading: busy, onClick: handleConfirm, variant: "primary", size: "small", "data-testid": "confirm", children: confirmLabel ?? 'Confirm' })] })] }) }));
56
+ }, children: [_jsxs(AlertDialogHeader, { className: "flex-row items-center", children: [_jsx(ConfirmationDialogIcon, { variant: variant }), _jsxs("div", { className: "flex flex-col gap-4", children: [title == null ||
57
+ title === false ||
58
+ title === true ||
59
+ title === '' ? (_jsx(AlertDialogTitle, { className: "sr-only", children: confirmLabel || 'Confirm' })) : (_jsx(AlertDialogTitle, { children: title })), description == null ? null : (_jsx(AlertDialogDescription, { children: description }))] })] }), pendingLabel ? (_jsx("span", { role: "status", className: "sr-only", children: busy ? pendingLabel : '' })) : null, _jsxs(AlertDialogFooter, { className: "gap-4", children: [_jsx(AlertDialogCancel, { asChild: true, variant: "secondary", children: _jsx(Button, { onClick: onCancel, disabled: busy, variant: "secondary", size: "small", children: cancelLabel ?? 'Cancel' }) }), _jsx(LoadingButton, { loading: busy, onClick: handleConfirm, variant: "primary", size: "small", "data-testid": "confirm", children: confirmLabel ?? 'Confirm' })] })] }) }));
57
60
  }
@@ -1,8 +1,24 @@
1
1
  import React from 'react';
2
2
  import { CollapsibleContentProps, CollapsibleProps, CollapsibleTriggerProps } from '@radix-ui/react-collapsible';
3
3
  declare function EntityBox({ className, children, ...props }: React.ComponentProps<'div'>): React.JSX.Element;
4
- declare function EntityBoxCollapsible({ className, ...props }: CollapsibleProps): React.JSX.Element;
5
- declare function EntityBoxCollapsibleTrigger(props: CollapsibleTriggerProps): React.JSX.Element;
4
+ declare function EntityBoxCollapsible({ className, open, defaultOpen, onOpenChange, ...props }: CollapsibleProps): React.JSX.Element;
5
+ export type EntityBoxCollapsibleTriggerProps = CollapsibleTriggerProps & {
6
+ /** Accessible name while the box is closed. */
7
+ expandLabel?: string;
8
+ /** Accessible name while the box is open. */
9
+ collapseLabel?: string;
10
+ };
11
+ /**
12
+ * ⛔ THIS TRIGGER HAS NO TEXT NODE. It is a `Settings2` glyph inside a Button,
13
+ * so without an `aria-label` a screen reader announced "button" and nothing
14
+ * else (SC 4.1.2) — and this is the control that opens the filter panel on
15
+ * every console list page.
16
+ *
17
+ * The name describes the ACTION and Radix's `aria-expanded` carries the state,
18
+ * so the two halves do not repeat each other. English defaults because no
19
+ * consumer passes a label today; a translated console overrides both.
20
+ */
21
+ declare function EntityBoxCollapsibleTrigger({ expandLabel, collapseLabel, 'aria-label': ariaLabel, ...props }: EntityBoxCollapsibleTriggerProps): React.JSX.Element;
6
22
  declare function EntityBoxCollapsibleContent({ children, ...props }: CollapsibleContentProps): React.JSX.Element;
7
23
  export interface EntityBoxHeaderProps extends React.ComponentProps<'div'> {
8
24
  title: string;
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/components/entity-box/index.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AAczB,OAAO,EACL,uBAAuB,EACvB,gBAAgB,EAChB,uBAAuB,EACxB,MAAM,6BAA6B,CAAA;AAIpC,iBAAS,SAAS,CAAC,EACjB,SAAS,EACT,QAAQ,EACR,GAAG,KAAK,EACT,EAAE,KAAK,CAAC,cAAc,CAAC,KAAK,CAAC,qBAa7B;AAED,iBAAS,oBAAoB,CAAC,EAAE,SAAS,EAAE,GAAG,KAAK,EAAE,EAAE,gBAAgB,qBAUtE;AAED,iBAAS,2BAA2B,CAAC,KAAK,EAAE,uBAAuB,qBAQlE;AAED,iBAAS,2BAA2B,CAAC,EACnC,QAAQ,EACR,GAAG,KAAK,EACT,EAAE,uBAAuB,qBAOzB;AAED,MAAM,WAAW,oBAAqB,SAAQ,KAAK,CAAC,cAAc,CAAC,KAAK,CAAC;IACvE,KAAK,EAAE,MAAM,CAAA;IACb,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,YAAY,CAAC,EAAE,MAAM,GAAG,MAAM,CAAA;IAC9B;;;;;;;OAOG;IACH,EAAE,CAAC,EAAE,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,CAAA;CAC/B;AAED,iBAAS,oBAAoB,CAAC,EAC5B,KAAK,EACL,QAAQ,EACR,OAAO,EACP,YAAY,EACZ,SAAS,EACT,EAAE,EAAE,OAAc,EAClB,GAAG,KAAK,EACT,EAAE,oBAAoB,qBAkCtB;AAED,iBAAS,eAAe,CAAC,EACvB,SAAS,EACT,QAAQ,EACR,GAAG,KAAK,EACT,EAAE,KAAK,CAAC,cAAc,CAAC,KAAK,CAAC,qBAU7B;AAED,iBAAS,gBAAgB,CAAC,EACxB,SAAS,EACT,QAAQ,EACR,GAAG,KAAK,EACT,EAAE,KAAK,CAAC,cAAc,CAAC,KAAK,CAAC,qBAa7B;AAED,OAAO,EACL,SAAS,EACT,oBAAoB,EACpB,2BAA2B,EAC3B,2BAA2B,EAC3B,oBAAoB,EACpB,eAAe,EACf,gBAAgB,EACjB,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/components/entity-box/index.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AAczB,OAAO,EACL,uBAAuB,EACvB,gBAAgB,EAChB,uBAAuB,EACxB,MAAM,6BAA6B,CAAA;AAIpC,iBAAS,SAAS,CAAC,EACjB,SAAS,EACT,QAAQ,EACR,GAAG,KAAK,EACT,EAAE,KAAK,CAAC,cAAc,CAAC,KAAK,CAAC,qBAa7B;AAYD,iBAAS,oBAAoB,CAAC,EAC5B,SAAS,EACT,IAAI,EACJ,WAAW,EACX,YAAY,EACZ,GAAG,KAAK,EACT,EAAE,gBAAgB,qBAoBlB;AAED,MAAM,MAAM,gCAAgC,GAAG,uBAAuB,GAAG;IACvE,+CAA+C;IAC/C,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,6CAA6C;IAC7C,aAAa,CAAC,EAAE,MAAM,CAAA;CACvB,CAAA;AAED;;;;;;;;;GASG;AACH,iBAAS,2BAA2B,CAAC,EACnC,WAAsB,EACtB,aAA0B,EAC1B,YAAY,EAAE,SAAS,EACvB,GAAG,KAAK,EACT,EAAE,gCAAgC,qBAclC;AAED,iBAAS,2BAA2B,CAAC,EACnC,QAAQ,EACR,GAAG,KAAK,EACT,EAAE,uBAAuB,qBAOzB;AAED,MAAM,WAAW,oBAAqB,SAAQ,KAAK,CAAC,cAAc,CAAC,KAAK,CAAC;IACvE,KAAK,EAAE,MAAM,CAAA;IACb,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,YAAY,CAAC,EAAE,MAAM,GAAG,MAAM,CAAA;IAC9B;;;;;;;OAOG;IACH,EAAE,CAAC,EAAE,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,CAAA;CAC/B;AAED,iBAAS,oBAAoB,CAAC,EAC5B,KAAK,EACL,QAAQ,EACR,OAAO,EACP,YAAY,EACZ,SAAS,EACT,EAAE,EAAE,OAAc,EAClB,GAAG,KAAK,EACT,EAAE,oBAAoB,qBAkCtB;AAED,iBAAS,eAAe,CAAC,EACvB,SAAS,EACT,QAAQ,EACR,GAAG,KAAK,EACT,EAAE,KAAK,CAAC,cAAc,CAAC,KAAK,CAAC,qBAU7B;AAED,iBAAS,gBAAgB,CAAC,EACxB,SAAS,EACT,QAAQ,EACR,GAAG,KAAK,EACT,EAAE,KAAK,CAAC,cAAc,CAAC,KAAK,CAAC,qBAa7B;AAED,OAAO,EACL,SAAS,EACT,oBAAoB,EACpB,2BAA2B,EAC3B,2BAA2B,EAC3B,oBAAoB,EACpB,eAAe,EACf,gBAAgB,EACjB,CAAA"}
@@ -1,4 +1,5 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import React from 'react';
2
3
  import { cn } from '../../lib/utils.js';
3
4
  import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '../ui/tooltip/index.js';
4
5
  import { CircleHelp, Settings2 } from 'lucide-react';
@@ -8,11 +9,35 @@ import { Separator } from '../ui/separator/index.js';
8
9
  function EntityBox({ className, children, ...props }) {
9
10
  return (_jsx("div", { "data-slot": "entity-box", className: cn('shadow-entity-box bg-card mb-2 flex items-center justify-between rounded-lg p-6', className), ...props, children: children }));
10
11
  }
11
- function EntityBoxCollapsible({ className, ...props }) {
12
- return (_jsx(Collapsible, { className: cn('shadow-entity-box bg-card mb-2 flex flex-col rounded-lg', className), ...props }));
12
+ /**
13
+ * The open state, republished for the trigger.
14
+ *
15
+ * Radix keeps it in a context it does not export, and the trigger one element
16
+ * down needs it to say what activating it will DO. Mirroring it here rather
17
+ * than forcing every consumer to lift the state: all ten console call sites
18
+ * render `<EntityBoxCollapsible>` uncontrolled.
19
+ */
20
+ const EntityBoxCollapsibleContext = React.createContext(false);
21
+ function EntityBoxCollapsible({ className, open, defaultOpen, onOpenChange, ...props }) {
22
+ const [uncontrolled, setUncontrolled] = React.useState(defaultOpen ?? false);
23
+ return (_jsx(EntityBoxCollapsibleContext.Provider, { value: open ?? uncontrolled, children: _jsx(Collapsible, { className: cn('shadow-entity-box bg-card mb-2 flex flex-col rounded-lg', className), open: open, defaultOpen: defaultOpen, onOpenChange: (next) => {
24
+ setUncontrolled(next);
25
+ onOpenChange?.(next);
26
+ }, ...props }) }));
13
27
  }
14
- function EntityBoxCollapsibleTrigger(props) {
15
- return (_jsx(CollapsibleTrigger, { ...props, asChild: true, children: _jsx(Button, { variant: "secondary", className: "h-[34px] w-[34px] p-2", children: _jsx(Settings2, { size: 16 }) }) }));
28
+ /**
29
+ * ⛔ THIS TRIGGER HAS NO TEXT NODE. It is a `Settings2` glyph inside a Button,
30
+ * so without an `aria-label` a screen reader announced "button" and nothing
31
+ * else (SC 4.1.2) — and this is the control that opens the filter panel on
32
+ * every console list page.
33
+ *
34
+ * The name describes the ACTION and Radix's `aria-expanded` carries the state,
35
+ * so the two halves do not repeat each other. English defaults because no
36
+ * consumer passes a label today; a translated console overrides both.
37
+ */
38
+ function EntityBoxCollapsibleTrigger({ expandLabel = 'Expand', collapseLabel = 'Collapse', 'aria-label': ariaLabel, ...props }) {
39
+ const open = React.useContext(EntityBoxCollapsibleContext);
40
+ return (_jsx(CollapsibleTrigger, { ...props, asChild: true, children: _jsx(Button, { variant: "secondary", className: "h-[34px] w-[34px] p-2", "aria-label": ariaLabel ?? (open ? collapseLabel : expandLabel), children: _jsx(Settings2, { size: 16, "aria-hidden": true }) }) }));
16
41
  }
17
42
  function EntityBoxCollapsibleContent({ children, ...props }) {
18
43
  return (_jsxs(CollapsibleContent, { ...props, children: [_jsx(Separator, { orientation: "horizontal" }), _jsx("div", { className: "grid w-full grid-cols-3 p-6", children: children })] }));
@@ -1,6 +1,6 @@
1
1
  import { ReactNode } from 'react';
2
2
  import { Control, FieldPathByValue, FieldValues } from 'react-hook-form';
3
- import { type FileUploadResult } from '../../../components/ui/file-upload/index.js';
3
+ import { type FileUploadError, type FileUploadLabels, type FileUploadResult } from '../../../components/ui/file-upload/index.js';
4
4
  import { type RenderableLabel } from '../../../components/form/renderable-label.js';
5
5
  type FileUploadFieldOwnProps<T extends FieldValues = FieldValues> = {
6
6
  control: Control<T>;
@@ -17,6 +17,15 @@ type FileUploadFieldOwnProps<T extends FieldValues = FieldValues> = {
17
17
  maxSizeBytes?: number;
18
18
  disabled?: boolean;
19
19
  className?: string;
20
+ /**
21
+ * Override the picker's user-visible copy. Threaded straight to FileUpload;
22
+ * omitted fields keep the English defaults. Note `labels.error` governs the
23
+ * PICKER's own size/type/read refusal only — resolver messages still come
24
+ * from the schema through FormMessage.
25
+ */
26
+ labels?: FileUploadLabels;
27
+ /** Observe size, type, and read failures after the form value is cleared. */
28
+ onError?: (error: FileUploadError) => void;
20
29
  /** Optional escape hatch to also observe the picked File/clear alongside the form's text value. */
21
30
  onSelect?: (result: FileUploadResult | null) => void;
22
31
  };
@@ -47,6 +56,6 @@ export type FileUploadFieldProps<T extends FieldValues = FieldValues> = FileUplo
47
56
  * rejection; on resolver failure the FormMessage announces. Both surfaces are
48
57
  * wired.
49
58
  */
50
- export declare const FileUploadField: <T extends FieldValues = FieldValues>({ control, name, label, description, tooltip, required, accept, maxSizeBytes, disabled, className, onSelect, "aria-label": ariaLabelProp }: FileUploadFieldProps<T>) => import("react").JSX.Element;
59
+ export declare const FileUploadField: <T extends FieldValues = FieldValues>({ control, name, label, description, tooltip, required, accept, maxSizeBytes, disabled, className, labels, onError, onSelect, "aria-label": ariaLabelProp }: FileUploadFieldProps<T>) => import("react").JSX.Element;
51
60
  export {};
52
61
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/components/form/file-upload-field/index.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAE,SAAS,EAAY,MAAM,OAAO,CAAA;AAC3C,OAAO,EAAE,OAAO,EAAE,gBAAgB,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAA;AAWxE,OAAO,EAAc,KAAK,gBAAgB,EAAE,MAAM,6BAA6B,CAAA;AAC/E,OAAO,EAEL,KAAK,eAAe,EACrB,MAAM,oCAAoC,CAAA;AAE3C,KAAK,uBAAuB,CAAC,CAAC,SAAS,WAAW,GAAG,WAAW,IAAI;IAClE,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,CAAA;IACnB;;;;OAIG;IACH,IAAI,EAAE,gBAAgB,CAAC,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC,CAAA;IAC7C,WAAW,CAAC,EAAE,SAAS,CAAA;IACvB,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,mGAAmG;IACnG,QAAQ,CAAC,EAAE,CAAC,MAAM,EAAE,gBAAgB,GAAG,IAAI,KAAK,IAAI,CAAA;CACrD,CAAA;AAED;;;;GAIG;AACH,MAAM,MAAM,oBAAoB,CAAC,CAAC,SAAS,WAAW,GAAG,WAAW,IAClE,uBAAuB,CAAC,CAAC,CAAC,GACxB,CACI;IAAE,KAAK,EAAE,eAAe,CAAC;IAAC,YAAY,CAAC,EAAE,MAAM,CAAA;CAAE,GACjD;IAAE,KAAK,CAAC,EAAE,KAAK,CAAC;IAAC,YAAY,EAAE,MAAM,CAAA;CAAE,CAC1C,CAAA;AAEL;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,eAAe,GAAI,CAAC,SAAS,WAAW,GAAG,WAAW,EAAE,4IAalE,oBAAoB,CAAC,CAAC,CAAC,gCA8EzB,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/components/form/file-upload-field/index.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAE,SAAS,EAAY,MAAM,OAAO,CAAA;AAC3C,OAAO,EAAE,OAAO,EAAE,gBAAgB,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAA;AAWxE,OAAO,EAEL,KAAK,eAAe,EACpB,KAAK,gBAAgB,EACrB,KAAK,gBAAgB,EACtB,MAAM,6BAA6B,CAAA;AACpC,OAAO,EAEL,KAAK,eAAe,EACrB,MAAM,oCAAoC,CAAA;AAE3C,KAAK,uBAAuB,CAAC,CAAC,SAAS,WAAW,GAAG,WAAW,IAAI;IAClE,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,CAAA;IACnB;;;;OAIG;IACH,IAAI,EAAE,gBAAgB,CAAC,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC,CAAA;IAC7C,WAAW,CAAC,EAAE,SAAS,CAAA;IACvB,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB;;;;;OAKG;IACH,MAAM,CAAC,EAAE,gBAAgB,CAAA;IACzB,6EAA6E;IAC7E,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,eAAe,KAAK,IAAI,CAAA;IAC1C,mGAAmG;IACnG,QAAQ,CAAC,EAAE,CAAC,MAAM,EAAE,gBAAgB,GAAG,IAAI,KAAK,IAAI,CAAA;CACrD,CAAA;AAED;;;;GAIG;AACH,MAAM,MAAM,oBAAoB,CAAC,CAAC,SAAS,WAAW,GAAG,WAAW,IAClE,uBAAuB,CAAC,CAAC,CAAC,GACxB,CACI;IAAE,KAAK,EAAE,eAAe,CAAC;IAAC,YAAY,CAAC,EAAE,MAAM,CAAA;CAAE,GACjD;IAAE,KAAK,CAAC,EAAE,KAAK,CAAC;IAAC,YAAY,EAAE,MAAM,CAAA;CAAE,CAC1C,CAAA;AAEL;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,eAAe,GAAI,CAAC,SAAS,WAAW,GAAG,WAAW,EAAE,6JAelE,oBAAoB,CAAC,CAAC,CAAC,gCAoFzB,CAAA"}
@@ -19,7 +19,7 @@ import { hasRenderableLabel } from '../../../components/form/renderable-label.js
19
19
  * rejection; on resolver failure the FormMessage announces. Both surfaces are
20
20
  * wired.
21
21
  */
22
- export const FileUploadField = ({ control, name, label, description, tooltip, required, accept, maxSizeBytes, disabled, className, onSelect, 'aria-label': ariaLabelProp }) => {
22
+ export const FileUploadField = ({ control, name, label, description, tooltip, required, accept, maxSizeBytes, disabled, className, labels, onError, onSelect, 'aria-label': ariaLabelProp }) => {
23
23
  const [localValue, setLocalValue] = useState(null);
24
24
  const showLabel = hasRenderableLabel(label);
25
25
  // An empty or whitespace-only aria-label is worse than none: it names the
@@ -51,10 +51,15 @@ export const FileUploadField = ({ control, name, label, description, tooltip, re
51
51
  // value carries nothing else to tell them apart. Fix needs a reset epoch
52
52
  // from react-hook-form, or a form value richer than the file text.
53
53
  const shown = localValue && field.value === localValue.text ? localValue : null;
54
- return (_jsxs(FormItem, { required: required, children: [showLabel && (_jsx(FormLabel, { extra: tooltip ? _jsx(FormTooltip, { children: tooltip }) : undefined, children: label })), _jsx(FormControl, { children: _jsx(FileUpload, { ref: field.ref, name: field.name, onBlur: field.onBlur, "aria-label": ariaLabel, accept: accept, maxSizeBytes: maxSizeBytes, disabled: disabled, className: className, value: shown, onSelect: (result) => {
54
+ return (_jsxs(FormItem, { required: required, children: [showLabel && (_jsx(FormLabel, { extra: tooltip ? _jsx(FormTooltip, { children: tooltip }) : undefined, children: label })), _jsx(FormControl, { children: _jsx(FileUpload, { ref: field.ref, name: field.name, onBlur: field.onBlur, "aria-label": ariaLabel, labels: labels, accept: accept, maxSizeBytes: maxSizeBytes, disabled: disabled, className: className, value: shown, onSelect: (result) => {
55
55
  setLocalValue(result);
56
56
  field.onChange(result?.text ?? '');
57
57
  onSelect?.(result);
58
- }, onError: () => field.onChange('') }) }), _jsx(FormMessage, {}), description && _jsx(FormDescription, { children: description })] }));
58
+ }, onError: (error) => {
59
+ setLocalValue(null);
60
+ field.onChange('');
61
+ onSelect?.(null);
62
+ onError?.(error);
63
+ } }) }), _jsx(FormMessage, {}), description && _jsx(FormDescription, { children: description })] }));
59
64
  } }));
60
65
  };
@@ -1,9 +1,48 @@
1
- import { HTMLInputTypeAttribute, ReactNode } from 'react';
1
+ import { type InputRef } from '../../../components/ui/input/index.js';
2
+ import { HTMLInputTypeAttribute, ReactNode, Ref } from 'react';
2
3
  import { Control, FieldValues } from 'react-hook-form';
3
4
  export type InputFieldProps<T extends FieldValues = FieldValues> = {
4
5
  className?: string;
5
6
  name: string;
6
7
  type?: HTMLInputTypeAttribute;
8
+ /**
9
+ * Announce the field's own invalidity. Reaches the rendered control on BOTH
10
+ * branches, so a field with no `control` — and therefore no react-hook-form
11
+ * error state — can still tell a screen reader it is invalid.
12
+ *
13
+ * ORed with the form's error, never overriding it: `false` here cannot
14
+ * suppress a live resolver error. Nobody asked to mute a real validation
15
+ * failure, and on a financial console a muted one is a wrong number reaching
16
+ * a money path.
17
+ *
18
+ * TypeScript exempts hyphenated JSX attributes from excess-property
19
+ * checking, so passing this used to compile and then vanish.
20
+ */
21
+ 'aria-invalid'?: boolean;
22
+ /** Lower bound, forwarded to the single-line control on both branches. */
23
+ min?: number | string;
24
+ /** Upper bound, forwarded to the single-line control on both branches. */
25
+ max?: number | string;
26
+ /** Increment, forwarded to the single-line control on both branches. */
27
+ step?: number | string;
28
+ /** Character ceiling, forwarded on both branches (single-line and textarea). */
29
+ maxLength?: number;
30
+ /** Browser autofill hint. `"off"` is the one a money field usually wants. */
31
+ autoComplete?: React.HTMLInputAutoCompleteAttribute;
32
+ /** Virtual-keyboard hint, e.g. `"numeric"` or `"decimal"`. Both branches. */
33
+ inputMode?: React.HTMLAttributes<HTMLElement>['inputMode'];
34
+ /** Native validation regex. Single-line only — a textarea has no `pattern`. */
35
+ pattern?: string;
36
+ /** Focus the control on mount. React focuses the node rather than emitting an attribute. */
37
+ autoFocus?: boolean;
38
+ /**
39
+ * Imperative handle on the rendered control: `focus()`/`blur()` on the
40
+ * single-line branch, the textarea node on the `textArea` branch (which
41
+ * satisfies the same shape). COMPOSED with react-hook-form's own ref when
42
+ * `control` is present, never replacing it — the form keeps the node it
43
+ * needs for `shouldFocusError`.
44
+ */
45
+ ref?: Ref<InputRef>;
7
46
  label?: ReactNode;
8
47
  tooltip?: string;
9
48
  labelExtra?: ReactNode;
@@ -32,5 +71,5 @@ export type InputFieldProps<T extends FieldValues = FieldValues> = {
32
71
  'data-testid'?: string;
33
72
  onChange?: (e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>) => void;
34
73
  };
35
- export declare const InputField: <T extends FieldValues = FieldValues>({ className, type, label, tooltip, labelExtra, placeholder, description, startAdornment, endAdornment, required, readOnly, minHeight, maxHeight, textArea, defaultValue, value, onChange, ...others }: InputFieldProps<T>) => import("react").JSX.Element;
74
+ export declare const InputField: <T extends FieldValues = FieldValues>({ className, type, label, tooltip, labelExtra, placeholder, description, startAdornment, endAdornment, required, readOnly, minHeight, maxHeight, textArea, defaultValue, value, onChange, min, max, step, maxLength, autoComplete, inputMode, pattern, autoFocus, ref, "aria-invalid": ariaInvalid, ...others }: InputFieldProps<T>) => import("react").JSX.Element;
36
75
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/components/form/input-field/index.tsx"],"names":[],"mappings":"AAWA,OAAO,EAAE,sBAAsB,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AACzD,OAAO,EAAE,OAAO,EAAkB,WAAW,EAAQ,MAAM,iBAAiB,CAAA;AAE5E,MAAM,MAAM,eAAe,CAAC,CAAC,SAAS,WAAW,GAAG,WAAW,IAAI;IACjE,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,CAAC,EAAE,sBAAsB,CAAA;IAC7B,KAAK,CAAC,EAAE,SAAS,CAAA;IACjB,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,UAAU,CAAC,EAAE,SAAS,CAAA;IACtB,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,WAAW,CAAC,EAAE,SAAS,CAAA;IACvB,cAAc,CAAC,EAAE,SAAS,CAAA;IAC1B,YAAY,CAAC,EAAE,SAAS,CAAA;IACxB;oDACgD;IAChD,OAAO,CAAC,EAAE,OAAO,CAAC,CAAC,CAAC,CAAA;IACpB,kDAAkD;IAClD,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB;;;;;OAKG;IACH,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,QAAQ,CAAC,EAAE,CACT,CAAC,EAAE,KAAK,CAAC,WAAW,CAAC,gBAAgB,GAAG,mBAAmB,CAAC,KACzD,IAAI,CAAA;CACV,CAAA;AAeD,eAAO,MAAM,UAAU,GAAI,CAAC,SAAS,WAAW,GAAG,WAAW,EAAE,uMAmB7D,eAAe,CAAC,CAAC,CAAC,gCA2EpB,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/components/form/input-field/index.tsx"],"names":[],"mappings":"AAUA,OAAO,EAAS,KAAK,QAAQ,EAAE,MAAM,uBAAuB,CAAA;AAC5D,OAAO,EAAE,sBAAsB,EAAE,SAAS,EAAE,GAAG,EAAmB,MAAM,OAAO,CAAA;AAC/E,OAAO,EAAE,OAAO,EAAkB,WAAW,EAAQ,MAAM,iBAAiB,CAAA;AAE5E,MAAM,MAAM,eAAe,CAAC,CAAC,SAAS,WAAW,GAAG,WAAW,IAAI;IACjE,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,CAAC,EAAE,sBAAsB,CAAA;IAC7B;;;;;;;;;;;;OAYG;IACH,cAAc,CAAC,EAAE,OAAO,CAAA;IACxB,0EAA0E;IAC1E,GAAG,CAAC,EAAE,MAAM,GAAG,MAAM,CAAA;IACrB,0EAA0E;IAC1E,GAAG,CAAC,EAAE,MAAM,GAAG,MAAM,CAAA;IACrB,wEAAwE;IACxE,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM,CAAA;IACtB,gFAAgF;IAChF,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,6EAA6E;IAC7E,YAAY,CAAC,EAAE,KAAK,CAAC,8BAA8B,CAAA;IACnD,6EAA6E;IAC7E,SAAS,CAAC,EAAE,KAAK,CAAC,cAAc,CAAC,WAAW,CAAC,CAAC,WAAW,CAAC,CAAA;IAC1D,+EAA+E;IAC/E,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,4FAA4F;IAC5F,SAAS,CAAC,EAAE,OAAO,CAAA;IACnB;;;;;;OAMG;IACH,GAAG,CAAC,EAAE,GAAG,CAAC,QAAQ,CAAC,CAAA;IACnB,KAAK,CAAC,EAAE,SAAS,CAAA;IACjB,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,UAAU,CAAC,EAAE,SAAS,CAAA;IACtB,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,WAAW,CAAC,EAAE,SAAS,CAAA;IACvB,cAAc,CAAC,EAAE,SAAS,CAAA;IAC1B,YAAY,CAAC,EAAE,SAAS,CAAA;IACxB;oDACgD;IAChD,OAAO,CAAC,EAAE,OAAO,CAAC,CAAC,CAAC,CAAA;IACpB,kDAAkD;IAClD,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB;;;;;OAKG;IACH,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,QAAQ,CAAC,EAAE,CACT,CAAC,EAAE,KAAK,CAAC,WAAW,CAAC,gBAAgB,GAAG,mBAAmB,CAAC,KACzD,IAAI,CAAA;CACV,CAAA;AA4CD,eAAO,MAAM,UAAU,GAAI,CAAC,SAAS,WAAW,GAAG,WAAW,EAAE,iTA6B7D,eAAe,CAAC,CAAC,CAAC,gCAyHpB,CAAA"}
@@ -2,8 +2,59 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { AutosizeTextarea } from '../../../components/ui/autosize-textarea/index.js';
3
3
  import { FormControl, FormDescription, FormField, FormItem, FormLabel, FormMessage, FormTooltip } from '../../../components/ui/form.js';
4
4
  import { Input } from '../../../components/ui/input/index.js';
5
- export const InputField = ({ className, type, label, tooltip, labelExtra, placeholder, description, startAdornment, endAdornment, required, readOnly, minHeight, maxHeight, textArea, defaultValue, value, onChange, ...others }) => {
6
- const renderItem = (binding) => (_jsxs(FormItem, { required: required, children: [label && (_jsx(FormLabel, { extra: tooltip ? _jsx(FormTooltip, { children: tooltip }) : labelExtra, children: label })), _jsx(FormControl, { children: textArea ? (_jsx(AutosizeTextarea, { className: className, placeholder: placeholder, readOnly: readOnly, minHeight: minHeight, maxHeight: maxHeight, "data-testid": others['data-testid'], ...binding })) : (_jsx(Input, { className: className, type: type, placeholder: placeholder, readOnly: readOnly, startAdornment: startAdornment, endAdornment: endAdornment, "data-testid": others['data-testid'], ...binding })) }), _jsx(FormMessage, {}), description && _jsx(FormDescription, { children: description })] }));
5
+ import { useMemo, useRef } from 'react';
6
+ /**
7
+ * Fan one node out to several refs. React hands a node to ONE ref, so a
8
+ * consumer ref and react-hook-form's `field.ref` cannot both be passed
9
+ * directly — one silently wins. Only used when the consumer actually supplied
10
+ * a ref, so a field without one keeps `field.ref`'s own identity and React
11
+ * never re-attaches it.
12
+ */
13
+ function fanOutRef(...refs) {
14
+ return (node) => {
15
+ const cleanups = [];
16
+ for (const ref of refs) {
17
+ if (typeof ref === 'function') {
18
+ const cleanup = ref(node);
19
+ cleanups.push(typeof cleanup === 'function' ? cleanup : () => ref(null));
20
+ }
21
+ else if (ref) {
22
+ ;
23
+ ref.current = node;
24
+ cleanups.push(() => {
25
+ ;
26
+ ref.current = null;
27
+ });
28
+ }
29
+ }
30
+ return () => cleanups.forEach((cleanup) => cleanup());
31
+ };
32
+ }
33
+ export const InputField = ({ className, type, label, tooltip, labelExtra, placeholder, description, startAdornment, endAdornment, required, readOnly, minHeight, maxHeight, textArea, defaultValue, value, onChange, min, max, step, maxLength, autoComplete, inputMode, pattern, autoFocus, ref, 'aria-invalid': ariaInvalid, ...others }) => {
34
+ // OR with the form's error, expressed through the Slot merge rather than by
35
+ // reading the form: FormControl already injects `aria-invalid={!!error}`, and
36
+ // a child prop only wins that merge when the KEY is present. So emit the key
37
+ // only for an explicit `true`, and let FormControl answer for every other
38
+ // case. `true || error` is true; `false || error` is whatever the form says,
39
+ // which is the whole point — an explicit `false` cannot mute a live error.
40
+ // With no `control` there is no error to OR against, so FormControl injects
41
+ // `false` and today's behaviour is unchanged.
42
+ const ariaInvalidProp = ariaInvalid ? { 'aria-invalid': true } : undefined;
43
+ const formRef = useRef(undefined);
44
+ const composedRef = useMemo(() => ref ? fanOutRef((node) => formRef.current?.(node), ref) : ref,
45
+ // `others.name` belongs here, and leaving it out is not a lint nit.
46
+ // react-hook-form hands a fresh `field.ref` for every name, and that ref
47
+ // only receives the element when React reattaches, which it does only when
48
+ // this callback's identity changes. Memoised on `ref` alone, one identity
49
+ // survived a name change, the new field never got its element, and
50
+ // `form.setFocus(newName)` then found nothing to focus and said nothing.
51
+ [ref, others.name]);
52
+ const renderItem = (binding) => (_jsxs(FormItem, { required: required, children: [label && (_jsx(FormLabel, { extra: tooltip ? _jsx(FormTooltip, { children: tooltip }) : labelExtra, children: label })), _jsx(FormControl, { children: textArea ? (_jsx(AutosizeTextarea, { className: className, placeholder: placeholder, readOnly: readOnly, minHeight: minHeight, maxHeight: maxHeight, maxLength: maxLength, autoComplete: autoComplete, inputMode: inputMode, autoFocus: autoFocus, "data-testid": others['data-testid'], ...ariaInvalidProp, ...binding })) : (_jsx(Input, { className: className, type: type, placeholder: placeholder, readOnly: readOnly, startAdornment: startAdornment, endAdornment: endAdornment,
53
+ // Bounds belong to the single-line control only: `min`/`max`/`step`
54
+ // mean nothing on a textarea and would render as invalid markup.
55
+ min: min, max: max, step: step, maxLength: maxLength, autoComplete: autoComplete, inputMode: inputMode,
56
+ // Single-line only: `<textarea pattern>` is not a thing.
57
+ pattern: pattern, autoFocus: autoFocus, "data-testid": others['data-testid'], ...ariaInvalidProp, ...binding })) }), _jsx(FormMessage, {}), description && _jsx(FormDescription, { children: description })] }));
7
58
  if (!others.control) {
8
59
  return renderItem({
9
60
  name: others.name,
@@ -11,7 +62,10 @@ export const InputField = ({ className, type, label, tooltip, labelExtra, placeh
11
62
  // Controlled when the caller holds the value, uncontrolled when it only
12
63
  // seeds one — supplying both is what React warns about.
13
64
  ...(value !== undefined ? { value } : { defaultValue }),
14
- onChange: (e) => onChange?.(e)
65
+ onChange: (e) => onChange?.(e),
66
+ // Nothing else claims the node on this branch, so the consumer ref goes
67
+ // straight through. Without it there was no node to focus at all.
68
+ ref: ref
15
69
  });
16
70
  }
17
71
  return (_jsx(FormField, { ...others, name: others.name,
@@ -19,16 +73,20 @@ export const InputField = ({ className, type, label, tooltip, labelExtra, placeh
19
73
  // value painted on the box only makes the field LOOK filled while the
20
74
  // form still submits nothing for that name. Controller seeds its own
21
75
  // state from this, so `field.value` carries the seed on both branches.
22
- defaultValue: defaultValue, render: ({ field }) => renderItem({
23
- ...field,
24
- // Controlled from mount. react-hook-form hands `undefined` for any
25
- // name the form declares no default for; React then mounts the box
26
- // uncontrolled and flips it on the first keystroke, losing that
27
- // character.
28
- value: field.value ?? '',
29
- onChange: (e) => {
30
- field.onChange(e);
31
- onChange?.(e);
32
- }
33
- }) }));
76
+ defaultValue: defaultValue, render: ({ field }) => {
77
+ formRef.current = field.ref;
78
+ return renderItem({
79
+ ...field,
80
+ // Controlled from mount. react-hook-form hands `undefined` for any
81
+ // name the form declares no default for; React then mounts the box
82
+ // uncontrolled and flips it on the first keystroke, losing that
83
+ // character.
84
+ value: field.value ?? '',
85
+ onChange: (e) => {
86
+ field.onChange(e);
87
+ onChange?.(e);
88
+ },
89
+ ref: (ref ? composedRef : field.ref)
90
+ });
91
+ } }));
34
92
  };
@@ -9,6 +9,10 @@ export type PasswordFieldProps<TFieldValues extends FieldValues = FieldValues, T
9
9
  required?: boolean;
10
10
  disabled?: boolean;
11
11
  'data-testid'?: string;
12
+ /** Accessible name of the eye button while the password is hidden. */
13
+ showPasswordLabel?: string;
14
+ /** Accessible name of the eye button while the password is showing. */
15
+ hidePasswordLabel?: string;
12
16
  };
13
- export declare function PasswordField<TFieldValues extends FieldValues = FieldValues, TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>>({ name, label, tooltip, placeholder, control, required, disabled, 'data-testid': dataTestId }: PasswordFieldProps<TFieldValues, TName>): React.JSX.Element;
17
+ export declare function PasswordField<TFieldValues extends FieldValues = FieldValues, TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>>({ name, label, tooltip, placeholder, control, required, disabled, 'data-testid': dataTestId, showPasswordLabel, hidePasswordLabel }: PasswordFieldProps<TFieldValues, TName>): React.JSX.Element;
14
18
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/components/form/password-field/index.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AACzB,OAAO,EAAE,OAAO,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAA;AAajE,MAAM,MAAM,kBAAkB,CAC5B,YAAY,SAAS,WAAW,GAAG,WAAW,EAC9C,KAAK,SAAS,SAAS,CAAC,YAAY,CAAC,GAAG,SAAS,CAAC,YAAY,CAAC,IAC7D;IACF,IAAI,EAAE,KAAK,CAAA;IACX,KAAK,EAAE,MAAM,CAAA;IACb,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,OAAO,EAAE,OAAO,CAAC,YAAY,CAAC,CAAA;IAC9B,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,aAAa,CAAC,EAAE,MAAM,CAAA;CACvB,CAAA;AAED,wBAAgB,aAAa,CAC3B,YAAY,SAAS,WAAW,GAAG,WAAW,EAC9C,KAAK,SAAS,SAAS,CAAC,YAAY,CAAC,GAAG,SAAS,CAAC,YAAY,CAAC,EAC/D,EACA,IAAI,EACJ,KAAK,EACL,OAAO,EACP,WAAW,EACX,OAAO,EACP,QAAgB,EAChB,QAAgB,EAChB,aAAa,EAAE,UAAU,EAC1B,EAAE,kBAAkB,CAAC,YAAY,EAAE,KAAK,CAAC,qBA0CzC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/components/form/password-field/index.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AACzB,OAAO,EAAE,OAAO,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAA;AAajE,MAAM,MAAM,kBAAkB,CAC5B,YAAY,SAAS,WAAW,GAAG,WAAW,EAC9C,KAAK,SAAS,SAAS,CAAC,YAAY,CAAC,GAAG,SAAS,CAAC,YAAY,CAAC,IAC7D;IACF,IAAI,EAAE,KAAK,CAAA;IACX,KAAK,EAAE,MAAM,CAAA;IACb,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,OAAO,EAAE,OAAO,CAAC,YAAY,CAAC,CAAA;IAC9B,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,sEAAsE;IACtE,iBAAiB,CAAC,EAAE,MAAM,CAAA;IAC1B,uEAAuE;IACvE,iBAAiB,CAAC,EAAE,MAAM,CAAA;CAC3B,CAAA;AAED,wBAAgB,aAAa,CAC3B,YAAY,SAAS,WAAW,GAAG,WAAW,EAC9C,KAAK,SAAS,SAAS,CAAC,YAAY,CAAC,GAAG,SAAS,CAAC,YAAY,CAAC,EAC/D,EACA,IAAI,EACJ,KAAK,EACL,OAAO,EACP,WAAW,EACX,OAAO,EACP,QAAgB,EAChB,QAAgB,EAChB,aAAa,EAAE,UAAU,EACzB,iBAAmC,EACnC,iBAAmC,EACpC,EAAE,kBAAkB,CAAC,YAAY,EAAE,KAAK,CAAC,qBAuDzC"}
@@ -4,7 +4,7 @@ import { FormControl, FormField, FormItem, FormLabel, FormMessage, FormTooltip }
4
4
  import { Input, InputAdornment } from '../../../components/ui/input/index.js';
5
5
  import { Eye, EyeOff } from 'lucide-react';
6
6
  import { IconButton } from '../../../components/ui/icon-button/index.js';
7
- export function PasswordField({ name, label, tooltip, placeholder, control, required = false, disabled = false, 'data-testid': dataTestId }) {
7
+ export function PasswordField({ name, label, tooltip, placeholder, control, required = false, disabled = false, 'data-testid': dataTestId, showPasswordLabel = 'Show password', hidePasswordLabel = 'Hide password' }) {
8
8
  const [show, setShow] = React.useState(false);
9
- return (_jsx(FormField, { control: control, name: name, render: ({ field }) => (_jsxs(FormItem, { required: required, children: [_jsx(FormLabel, { extra: tooltip ? _jsx(FormTooltip, { children: tooltip }) : undefined, children: label }), _jsx(FormControl, { children: _jsx(Input, { ...field, type: show ? 'text' : 'password', placeholder: placeholder, endAdornment: _jsx(InputAdornment, { position: "end", children: _jsx(IconButton, { type: "button", variant: "outline", rounded: true, onClick: () => setShow(!show), children: show ? _jsx(EyeOff, {}) : _jsx(Eye, {}) }) }), disabled: disabled, className: "pr-10", "data-testid": dataTestId }) }), _jsx(FormMessage, {})] })) }));
9
+ return (_jsx(FormField, { control: control, name: name, render: ({ field }) => (_jsxs(FormItem, { required: required, children: [_jsx(FormLabel, { extra: tooltip ? _jsx(FormTooltip, { children: tooltip }) : undefined, children: label }), _jsx(FormControl, { children: _jsx(Input, { ...field, type: show ? 'text' : 'password', placeholder: placeholder, endAdornment: _jsx(InputAdornment, { position: "end", children: _jsx(IconButton, { type: "button", variant: "outline", rounded: true, "aria-label": show ? hidePasswordLabel : showPasswordLabel, onClick: () => setShow(!show), children: show ? _jsx(EyeOff, { "aria-hidden": true }) : _jsx(Eye, { "aria-hidden": true }) }) }), disabled: disabled, className: "pr-10", "data-testid": dataTestId }) }), _jsx(FormMessage, {})] })) }));
10
10
  }