@godxjp/ui 20.0.0 → 20.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (273) hide show
  1. package/README.md +8 -2
  2. package/dist/app/app-provider.js +37 -21
  3. package/dist/app/index.d.ts +2 -0
  4. package/dist/app/index.js +2 -0
  5. package/dist/app/storage.d.ts +17 -0
  6. package/dist/app/storage.js +28 -0
  7. package/dist/app/theme-axes.d.ts +2 -0
  8. package/dist/app/theme-axes.js +45 -0
  9. package/dist/components/charts/chart-cartesian.d.ts +2 -1
  10. package/dist/components/charts/chart-cartesian.js +52 -3
  11. package/dist/components/charts/chart-category-axis.d.ts +55 -0
  12. package/dist/components/charts/chart-category-axis.js +93 -0
  13. package/dist/components/charts/chart-frame.d.ts +10 -1
  14. package/dist/components/charts/chart-frame.js +19 -3
  15. package/dist/components/charts/compact-bar-trend.d.ts +1 -1
  16. package/dist/components/charts/compact-bar-trend.js +2 -0
  17. package/dist/components/charts/pie-chart.d.ts +1 -1
  18. package/dist/components/charts/pie-chart.js +12 -1
  19. package/dist/components/charts/recharts-peer.d.ts +49 -0
  20. package/dist/components/charts/recharts-peer.js +45 -0
  21. package/dist/components/data-display/card.d.ts +12 -1
  22. package/dist/components/data-display/card.js +22 -4
  23. package/dist/components/data-display/code-block.js +6 -2
  24. package/dist/components/data-display/data-table.d.ts +39 -4
  25. package/dist/components/data-display/data-table.js +802 -270
  26. package/dist/components/data-display/descriptions.d.ts +27 -6
  27. package/dist/components/data-display/descriptions.js +58 -17
  28. package/dist/components/data-display/index.d.ts +2 -0
  29. package/dist/components/data-display/index.js +2 -0
  30. package/dist/components/data-display/list-row.d.ts +10 -1
  31. package/dist/components/data-display/list-row.js +7 -1
  32. package/dist/components/data-display/popover.d.ts +9 -3
  33. package/dist/components/data-display/popover.js +65 -11
  34. package/dist/components/data-display/range-timeline.d.ts +38 -0
  35. package/dist/components/data-display/range-timeline.js +161 -0
  36. package/dist/components/data-display/scroll-area.js +13 -2
  37. package/dist/components/data-display/service-launcher-card.js +14 -12
  38. package/dist/components/data-display/table.d.ts +71 -3
  39. package/dist/components/data-display/table.js +56 -18
  40. package/dist/components/data-display/tree-list.js +4 -9
  41. package/dist/components/data-entry/calendar.d.ts +2 -2
  42. package/dist/components/data-entry/calendar.js +23 -3
  43. package/dist/components/data-entry/cascader.d.ts +1 -1
  44. package/dist/components/data-entry/cascader.js +188 -24
  45. package/dist/components/data-entry/color-picker.d.ts +1 -1
  46. package/dist/components/data-entry/color-picker.js +17 -5
  47. package/dist/components/data-entry/control-appearance.d.ts +64 -0
  48. package/dist/components/data-entry/control-appearance.js +39 -0
  49. package/dist/components/data-entry/control-surface.d.ts +61 -0
  50. package/dist/components/data-entry/control-surface.js +39 -0
  51. package/dist/components/data-entry/date-picker.d.ts +1 -1
  52. package/dist/components/data-entry/date-picker.js +344 -113
  53. package/dist/components/data-entry/date-range-picker.d.ts +1 -1
  54. package/dist/components/data-entry/date-range-picker.js +278 -140
  55. package/dist/components/data-entry/form-field.d.ts +1 -1
  56. package/dist/components/data-entry/form-field.js +39 -4
  57. package/dist/components/data-entry/form.d.ts +4 -0
  58. package/dist/components/data-entry/form.js +4 -2
  59. package/dist/components/data-entry/index.d.ts +7 -3
  60. package/dist/components/data-entry/index.js +10 -1
  61. package/dist/components/data-entry/input-otp.d.ts +1 -1
  62. package/dist/components/data-entry/input.d.ts +12 -34
  63. package/dist/components/data-entry/input.js +92 -24
  64. package/dist/components/data-entry/month-picker.d.ts +1 -1
  65. package/dist/components/data-entry/month-picker.js +47 -10
  66. package/dist/components/data-entry/month-range-picker.d.ts +1 -1
  67. package/dist/components/data-entry/month-range-picker.js +51 -11
  68. package/dist/components/data-entry/number-input.d.ts +7 -0
  69. package/dist/components/data-entry/number-input.js +147 -98
  70. package/dist/components/data-entry/password-input.d.ts +1 -1
  71. package/dist/components/data-entry/radio.d.ts +1 -1
  72. package/dist/components/data-entry/radio.js +61 -13
  73. package/dist/components/data-entry/search-input.d.ts +1 -1
  74. package/dist/components/data-entry/search-input.js +16 -2
  75. package/dist/components/data-entry/search-select.d.ts +2 -2
  76. package/dist/components/data-entry/search-select.js +209 -78
  77. package/dist/components/data-entry/select.d.ts +22 -4
  78. package/dist/components/data-entry/select.js +215 -163
  79. package/dist/components/data-entry/slider.d.ts +9 -1
  80. package/dist/components/data-entry/slider.js +87 -9
  81. package/dist/components/data-entry/switch.d.ts +3 -0
  82. package/dist/components/data-entry/switch.js +29 -3
  83. package/dist/components/data-entry/textarea.d.ts +14 -56
  84. package/dist/components/data-entry/textarea.js +80 -33
  85. package/dist/components/data-entry/time-picker.d.ts +2 -2
  86. package/dist/components/data-entry/time-picker.js +333 -109
  87. package/dist/components/data-entry/time-range-picker.d.ts +5 -0
  88. package/dist/components/data-entry/time-range-picker.js +89 -0
  89. package/dist/components/data-entry/transfer.d.ts +1 -1
  90. package/dist/components/data-entry/transfer.js +86 -28
  91. package/dist/components/data-entry/tree-select.d.ts +1 -1
  92. package/dist/components/data-entry/tree-select.js +201 -113
  93. package/dist/components/data-entry/tree-utils.js +7 -14
  94. package/dist/components/data-entry/upload-files.d.ts +2 -0
  95. package/dist/components/data-entry/upload-files.js +31 -0
  96. package/dist/components/data-entry/upload-request.d.ts +4 -0
  97. package/dist/components/data-entry/upload-request.js +53 -0
  98. package/dist/components/data-entry/upload-types.d.ts +28 -0
  99. package/dist/components/data-entry/upload-types.js +2 -0
  100. package/dist/components/data-entry/upload.d.ts +2 -2
  101. package/dist/components/data-entry/upload.js +475 -115
  102. package/dist/components/feedback/dialog.js +19 -19
  103. package/dist/components/feedback/sheet.js +3 -0
  104. package/dist/components/feedback/tooltip.js +3 -0
  105. package/dist/components/general/button.d.ts +2 -1
  106. package/dist/components/general/button.js +7 -1
  107. package/dist/components/general/index.d.ts +1 -0
  108. package/dist/components/general/index.js +2 -0
  109. package/dist/components/general/typography.d.ts +3 -0
  110. package/dist/components/general/typography.js +15 -1
  111. package/dist/components/general/visually-hidden.d.ts +3 -0
  112. package/dist/components/general/visually-hidden.js +9 -0
  113. package/dist/components/layout/app-launcher.d.ts +34 -0
  114. package/dist/components/layout/app-launcher.js +261 -0
  115. package/dist/components/layout/app-shell.d.ts +3 -1
  116. package/dist/components/layout/app-shell.js +34 -14
  117. package/dist/components/layout/auth-shell.d.ts +1 -1
  118. package/dist/components/layout/auth-shell.js +2 -0
  119. package/dist/components/layout/breadcrumb.d.ts +14 -2
  120. package/dist/components/layout/breadcrumb.js +46 -4
  121. package/dist/components/layout/flex.d.ts +1 -1
  122. package/dist/components/layout/flex.js +23 -2
  123. package/dist/components/layout/index.d.ts +3 -0
  124. package/dist/components/layout/index.js +5 -1
  125. package/dist/components/layout/org-switcher.js +20 -1
  126. package/dist/components/layout/page-container.d.ts +1 -1
  127. package/dist/components/layout/page-container.js +5 -3
  128. package/dist/components/layout/responsive-grid.d.ts +16 -2
  129. package/dist/components/layout/responsive-grid.js +29 -2
  130. package/dist/components/layout/sidebar.js +2 -1
  131. package/dist/components/layout/topbar-item.d.ts +3 -0
  132. package/dist/components/layout/topbar-item.js +18 -3
  133. package/dist/components/layout/topbar.d.ts +1 -1
  134. package/dist/components/layout/topbar.js +27 -6
  135. package/dist/components/navigation/dropdown-menu.d.ts +30 -2
  136. package/dist/components/navigation/dropdown-menu.js +73 -11
  137. package/dist/components/navigation/pagination.d.ts +2 -2
  138. package/dist/components/navigation/pagination.js +155 -83
  139. package/dist/components/navigation/steps.d.ts +2 -2
  140. package/dist/components/navigation/steps.js +29 -4
  141. package/dist/components/navigation/tabs.d.ts +5 -33
  142. package/dist/components/navigation/tabs.js +232 -64
  143. package/dist/components/ui/hover-card.js +3 -0
  144. package/dist/components/ui/input-otp.d.ts +26 -28
  145. package/dist/components/ui/input-otp.js +50 -7
  146. package/dist/components/ui/password-input.d.ts +36 -2
  147. package/dist/components/ui/password-input.js +27 -7
  148. package/dist/components/ui/rating.d.ts +25 -0
  149. package/dist/components/ui/rating.js +67 -27
  150. package/dist/components/ui/segmented.d.ts +9 -0
  151. package/dist/components/ui/segmented.js +17 -2
  152. package/dist/components/ui/separator.d.ts +4 -0
  153. package/dist/components/ui/separator.js +13 -0
  154. package/dist/components/ui/tag-input.d.ts +45 -0
  155. package/dist/components/ui/tag-input.js +109 -27
  156. package/dist/form/form-context.d.ts +29 -0
  157. package/dist/form/form-context.js +44 -4
  158. package/dist/form/form-field-array.d.ts +22 -0
  159. package/dist/form/form-field-array.js +50 -0
  160. package/dist/form/form-field-control.d.ts +2 -1
  161. package/dist/form/form-field-control.js +116 -40
  162. package/dist/form/form-root.d.ts +2 -1
  163. package/dist/form/form-root.js +142 -23
  164. package/dist/form/index.d.ts +3 -1
  165. package/dist/form/index.js +12 -1
  166. package/dist/i18n/index.d.ts +1 -1
  167. package/dist/i18n/index.js +3 -1
  168. package/dist/i18n/messages/en.json +59 -6
  169. package/dist/i18n/messages/ja.json +59 -6
  170. package/dist/i18n/messages/vi.json +59 -6
  171. package/dist/i18n/translate.d.ts +19 -0
  172. package/dist/i18n/translate.js +24 -0
  173. package/dist/inertia/index.d.ts +20 -0
  174. package/dist/inertia/index.js +60 -1
  175. package/dist/lib/control-styles.d.ts +25 -3
  176. package/dist/lib/control-styles.js +9 -3
  177. package/dist/lib/datetime/picker-format.d.ts +8 -0
  178. package/dist/lib/datetime/picker-format.js +54 -0
  179. package/dist/lib/overlay-portal.d.ts +18 -0
  180. package/dist/lib/overlay-portal.js +17 -0
  181. package/dist/props/components/app.prop.d.ts +13 -2
  182. package/dist/props/components/charts.prop.d.ts +21 -0
  183. package/dist/props/components/data-display.prop.d.ts +55 -4
  184. package/dist/props/components/data-entry.prop.d.ts +687 -43
  185. package/dist/props/components/form.prop.d.ts +142 -4
  186. package/dist/props/components/general.prop.d.ts +30 -1
  187. package/dist/props/components/index.d.ts +2 -2
  188. package/dist/props/components/layout.prop.d.ts +255 -8
  189. package/dist/props/components/navigation.prop.d.ts +113 -3
  190. package/dist/props/registry.d.ts +376 -10
  191. package/dist/props/registry.js +493 -9
  192. package/dist/props/vocabulary/data.prop.d.ts +179 -1
  193. package/dist/props/vocabulary/index.d.ts +4 -4
  194. package/dist/props/vocabulary/interaction.prop.d.ts +51 -2
  195. package/dist/props/vocabulary/navigation.prop.d.ts +40 -0
  196. package/dist/props/vocabulary/shared.prop.d.ts +33 -0
  197. package/dist/styles/badge-layout.css +2 -1
  198. package/dist/styles/card-layout.css +36 -5
  199. package/dist/styles/chart-layout.css +17 -1
  200. package/dist/styles/control.css +520 -14
  201. package/dist/styles/data-display-layout.css +248 -7
  202. package/dist/styles/data-entry-layout.css +13 -0
  203. package/dist/styles/layout.css +221 -4
  204. package/dist/styles/navigation-layout.css +230 -1
  205. package/dist/styles/shell-layout.css +690 -21
  206. package/dist/styles/table-layout.css +95 -6
  207. package/dist/styles/text-layout.css +5 -0
  208. package/dist/tokens/base.css +1 -0
  209. package/dist/tokens/components/badge.css +1 -0
  210. package/dist/tokens/components/chart.css +6 -0
  211. package/dist/tokens/components/control.css +81 -3
  212. package/dist/tokens/components/data-display.css +9 -0
  213. package/dist/tokens/components/descriptions.css +11 -0
  214. package/dist/tokens/components/flex.css +8 -0
  215. package/dist/tokens/components/navigation.css +55 -0
  216. package/dist/tokens/components/shell.css +100 -5
  217. package/dist/tokens/components/table.css +14 -0
  218. package/dist/tokens/foundation.css +2 -0
  219. package/dist/tokens/semantic/layout.css +9 -0
  220. package/docs/CONSUMER-RULES.md +13 -0
  221. package/docs/DESIGN-AUTHORITY.md +43 -6
  222. package/docs/DEVELOPMENT.md +4 -3
  223. package/docs/FORMS.md +151 -6
  224. package/docs/FRAME-COVERAGE-REPORT.md +29 -16
  225. package/docs/README.md +1 -1
  226. package/docs/STANDARDS-vocabulary-tokens.md +1 -1
  227. package/docs/TESTING.md +15 -6
  228. package/docs/charts/cjk-category-axis.tsx +81 -0
  229. package/docs/data-display/card/index.tsx +22 -0
  230. package/docs/data-display/data-table/examples/antd-parity.tsx +257 -0
  231. package/docs/data-display/data-table/index.tsx +23 -0
  232. package/docs/data-display/descriptions.tsx +41 -0
  233. package/docs/data-display/list-row.tsx +6 -6
  234. package/docs/data-display/scroll-area.tsx +31 -25
  235. package/docs/data-display/table.tsx +104 -45
  236. package/docs/data-display/timeline.tsx +41 -0
  237. package/docs/data-entry/date-picker.tsx +85 -0
  238. package/docs/data-entry/date-range-picker.tsx +26 -0
  239. package/docs/data-entry/form-dynamic-fields.tsx +199 -0
  240. package/docs/data-entry/form.tsx +74 -4
  241. package/docs/data-entry/input-otp.tsx +24 -0
  242. package/docs/data-entry/input.tsx +46 -1
  243. package/docs/data-entry/month-range-picker.tsx +1 -1
  244. package/docs/data-entry/select.tsx +27 -0
  245. package/docs/data-entry/switch.tsx +41 -0
  246. package/docs/data-entry/textarea.tsx +38 -0
  247. package/docs/data-entry/time-picker.tsx +85 -1
  248. package/docs/data-entry/time-range-picker.tsx +55 -0
  249. package/docs/data-entry/transfer.tsx +17 -0
  250. package/docs/data-entry/upload.tsx +56 -12
  251. package/docs/feedback/tooltip.tsx +1 -1
  252. package/docs/general/activity.tsx +2 -2
  253. package/docs/general/button/index.tsx +14 -1
  254. package/docs/general/typography.tsx +14 -1
  255. package/docs/layout/app-launcher.tsx +195 -0
  256. package/docs/layout/app-shell-arrangements.tsx +5 -5
  257. package/docs/layout/app-shell.tsx +11 -0
  258. package/docs/layout/flex.tsx +50 -0
  259. package/docs/layout/responsive-grid.tsx +21 -1
  260. package/docs/layout/topbar.tsx +5 -9
  261. package/docs/navigation/app-setting-picker.tsx +11 -0
  262. package/docs/navigation/breadcrumb.tsx +48 -0
  263. package/docs/navigation/dropdown-menu.tsx +21 -0
  264. package/docs/navigation/pagination.tsx +52 -0
  265. package/docs/navigation/steps.tsx +37 -0
  266. package/docs/navigation/tabs.tsx +97 -1
  267. package/docs/query/button-refetch.tsx +1 -0
  268. package/docs/showcase/settings-security-mfa.tsx +3 -3
  269. package/package.json +24 -4
  270. package/scripts/_agent-setup.mjs +76 -9
  271. package/scripts/init-guinea-pig.mjs +26 -4
  272. package/scripts/ui-audit.mjs +243 -30
  273. package/scripts/visual-audit.mjs +45 -7
@@ -1,42 +1,14 @@
1
1
  import * as React from "react";
2
- export type TabsItem = {
3
- value: string;
4
- label: React.ReactNode;
5
- content: React.ReactNode;
6
- disabled?: boolean;
7
- };
2
+ import type { TabItemProp, TabsProp } from "../../props/components/navigation.prop.js";
3
+ export type { TabItemProp, TabsProp, TabsVariantProp, TabsPlacementProp, TabsExtraProp, } from "../../props/components/navigation.prop.js";
8
4
  export type TabsOrientation = "vertical" | "horizontal";
9
- /**
10
- * The repo's Radix-era vocabulary, kept VERBATIM. React Aria spells the same four things
11
- * `selectedKey` / `defaultSelectedKey` / `onSelectionChange` / `keyboardActivation`; the
12
- * translation lives inside this file so no consumer ever has to learn RAC's names.
13
- *
14
- * `activationMode` is the one that MUST NOT be dropped. `.ai/rules/settings.md` in the consumer
15
- * requires `orientation="vertical"` + `activationMode="manual"` for the settings rail, because
16
- * every item there is a URL — automatic activation would load four pages while the reader is only
17
- * arrowing past them.
18
- */
19
- export type TabsProps = Omit<React.ComponentPropsWithoutRef<"div">, "defaultValue" | "dir"> & {
20
- /** Controlled selected tab — RAC `selectedKey`. */
21
- value?: string;
22
- /** Uncontrolled initial selection — RAC `defaultSelectedKey`. */
23
- defaultValue?: string;
24
- /** Fired with the NEW selected value — RAC `onSelectionChange`. */
25
- onValueChange?: (value: string) => void;
5
+ export type TabsItem = TabItemProp;
6
+ export type TabsProps = Omit<React.ComponentPropsWithoutRef<"div">, "defaultValue" | "dir"> & TabsProp & {
26
7
  orientation?: TabsOrientation;
27
- /**
28
- * DOM attribute only. Under Radix this also steered RTL arrow keys; React Aria reads the
29
- * direction from the locale, so an RTL app must set it through `I18nProvider`.
30
- */
31
8
  dir?: "ltr" | "rtl";
32
- /** `"manual"` = arrow keys move focus without selecting — RAC `keyboardActivation`. */
33
9
  activationMode?: "manual" | "automatic";
34
- items?: TabsItem[];
35
- variant?: "default" | "line" | "card";
36
- listClassName?: string;
37
- contentClassName?: string;
38
10
  };
39
- export declare function Tabs({ className, orientation, items, value, defaultValue, onValueChange, activationMode, dir, variant, listClassName, contentClassName, children, ...props }: TabsProps): React.JSX.Element;
11
+ export declare function Tabs({ className, orientation, items, value, defaultValue, onValueChange, activationMode, dir, variant, tabPlacement, size, centered, extra, destroyOnHidden, onEdit, addIcon, hideAdd, listClassName, contentClassName, children, ...props }: TabsProps): React.JSX.Element;
40
12
  export declare const TabsList: React.ForwardRefExoticComponent<Omit<React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>, "ref"> & {
41
13
  variant?: "default" | "line";
42
14
  /**
@@ -7,6 +7,8 @@ import {
7
7
  TabPanel as AriaTabPanel,
8
8
  Tabs as AriaTabs
9
9
  } from "react-aria-components";
10
+ import { Plus, X } from "lucide-react";
11
+ import { useTranslation } from "../../i18n/use-translation.js";
10
12
  import { cn } from "../../lib/utils.js";
11
13
  import { useKeepActiveTabVisible } from "./tabs-scroll.js";
12
14
  const TabsFrameContext = React.createContext({
@@ -31,9 +33,27 @@ function withDomProps(element, slot, raw, domProps, own) {
31
33
  ...own
32
34
  });
33
35
  }
36
+ function resolveTabsExtra(extra) {
37
+ if (extra === void 0 || extra === null || extra === false) return {};
38
+ const isSlotMap = typeof extra === "object" && !React.isValidElement(extra) && !Array.isArray(extra) && ("start" in extra || "end" in extra);
39
+ if (isSlotMap) return extra;
40
+ return { end: extra };
41
+ }
42
+ function resolveTabsAxis(tabPlacement, orientation) {
43
+ if (tabPlacement) {
44
+ return {
45
+ placement: tabPlacement,
46
+ orientation: orientation ?? (tabPlacement === "start" || tabPlacement === "end" ? "vertical" : "horizontal")
47
+ };
48
+ }
49
+ return {
50
+ placement: orientation === "vertical" ? "start" : "top",
51
+ orientation: orientation ?? "horizontal"
52
+ };
53
+ }
34
54
  function Tabs({
35
55
  className,
36
- orientation = "horizontal",
56
+ orientation,
37
57
  items,
38
58
  value,
39
59
  defaultValue,
@@ -41,95 +61,235 @@ function Tabs({
41
61
  activationMode,
42
62
  dir,
43
63
  variant = "default",
64
+ tabPlacement,
65
+ size = "md",
66
+ centered,
67
+ extra,
68
+ destroyOnHidden = true,
69
+ onEdit,
70
+ addIcon,
71
+ hideAdd,
44
72
  listClassName,
45
73
  contentClassName,
46
74
  children,
47
75
  ...props
48
76
  }) {
77
+ const { t } = useTranslation();
49
78
  const resolvedDefault = resolveFallbackTabValue(items, defaultValue);
79
+ const { placement, orientation: resolvedOrientation } = resolveTabsAxis(
80
+ tabPlacement,
81
+ orientation
82
+ );
50
83
  const selectionSuppressed = value === void 0 && items != null && items.length > 0 && resolvedDefault === void 0;
51
84
  const frame = React.useMemo(
52
- () => ({ orientation, selectionSuppressed }),
53
- [orientation, selectionSuppressed]
85
+ () => ({ orientation: resolvedOrientation, selectionSuppressed }),
86
+ [resolvedOrientation, selectionSuppressed]
87
+ );
88
+ const editable = variant === "editable-card";
89
+ const [mirroredValue, setMirroredValue] = React.useState(
90
+ value ?? resolvedDefault
91
+ );
92
+ React.useEffect(() => {
93
+ if (value !== void 0) setMirroredValue(value);
94
+ }, [value]);
95
+ const activeValue = value ?? mirroredValue;
96
+ const handleValueChange = React.useCallback(
97
+ (next) => {
98
+ setMirroredValue(next);
99
+ onValueChange?.(next);
100
+ },
101
+ [onValueChange]
54
102
  );
103
+ const { start: extraStart, end: extraEnd } = resolveTabsExtra(extra);
104
+ const showAdd = editable && !hideAdd && Boolean(onEdit);
105
+ const needsBar = Boolean(extraStart || extraEnd || showAdd);
106
+ const card = variant === "card" || variant === "editable-card";
107
+ const sizeTriggerClassName = cn(
108
+ size === "sm" && "min-h-[var(--tabs-trigger-height-sm)] px-[var(--tabs-trigger-padding-x-sm)] text-[length:var(--tabs-trigger-font-size-sm)]",
109
+ size === "md" && "min-h-[var(--tabs-trigger-height-md)] px-[var(--tabs-trigger-padding-x-md)] text-[length:var(--tabs-trigger-font-size-md)]",
110
+ size === "lg" && "min-h-[var(--tabs-trigger-height-lg)] px-[var(--tabs-trigger-padding-x-lg)] text-[length:var(--tabs-trigger-font-size-lg)]"
111
+ );
112
+ const list = items ? /* @__PURE__ */ jsx(
113
+ TabsList,
114
+ {
115
+ "data-slot": "tabs-list",
116
+ variant: variant === "line" ? "line" : "default",
117
+ className: cn(
118
+ // The inset override is an arbitrary value reading the knob (never `p-0`): it must
119
+ // stay a UTILITY so tailwind-merge still drops the strip's own padding step.
120
+ //
121
+ // IT IS SPLIT INTO `px-`/`py-` FOR THE FOCUS RING (gh#376). The strip clips its
122
+ // block axis (`overflow-y: hidden` beside `overflow-x: auto`), and this variant's
123
+ // inset is 0px, so a focused trigger had NO block headroom at all — measured in
124
+ // Chromium, 0px top and bottom, i.e. the entire ring was shaved. The headroom has to
125
+ // come from layout: `overflow-y: visible` next to `overflow-x: auto` computes back
126
+ // to `auto` per spec (a second scroll container, not a ring), and Chromium honours
127
+ // `overflow-clip-margin` only when BOTH axes are `clip`. So the block padding carries
128
+ // the ring's outer reach and an equal negative block margin hands it straight back —
129
+ // the ring paints inside the scrollport and the strip's OUTER box is unchanged
130
+ // (measured: 38px before and after). Both values read the same token the ring is
131
+ // sized from, so the two can never drift apart.
132
+ //
133
+ // The cost, recorded rather than hidden: the rail hairline (`border-b`, drawn at the
134
+ // border box) now sits the ring's reach below the triggers instead of flush. That is
135
+ // where a hand-composed <TabsList variant="line"> already put it — its `p-1` gives
136
+ // the same block inset — so the two construction paths now agree. A service that
137
+ // wants the active bar parked back on the hairline raises `--tabs-indicator-offset`,
138
+ // which exists for exactly that.
139
+ variant === "line" && "my-[calc(-1_*_var(--tabs-list-focus-ring-space-inset,calc(var(--focus-ring-width)_+_var(--focus-ring-glow-width))))] h-auto w-full justify-start border-b px-[var(--tabs-list-line-space-inset)] py-[calc(var(--tabs-list-line-space-inset)_+_var(--tabs-list-focus-ring-space-inset,calc(var(--focus-ring-width)_+_var(--focus-ring-glow-width))))]",
140
+ // CARD strip. The list keeps `data-variant="default"` on purpose (a hand-composed
141
+ // <TabsList> must be unaffected), so the card face is selected from the ROOT — but the
142
+ // three properties the base list already claims as utilities (`bg-muted`, `p-1`,
143
+ // `rounded-lg`) have to be replaced with utilities too, or the components layer loses.
144
+ card && "w-full items-end justify-start gap-[var(--tabs-card-list-space-gap)] rounded-[var(--tabs-card-list-radius)] p-[var(--tabs-card-list-space-inset)] data-[variant=default]:bg-transparent",
145
+ // CENTERED. Two independent moves, because the strip has two shapes: the pill/card strip
146
+ // is `w-fit` (auto inline margins centre the BOX) and the line strip is `w-full`
147
+ // (`justify-content` centres its CONTENT). `safe` keeps the overflow rule the strip
148
+ // already depends on — plain `center` strands the leading tab outside the scrollport.
149
+ centered && "mx-auto justify-center-safe",
150
+ listClassName
151
+ ),
152
+ children: items.map((item) => {
153
+ const removable = editable && Boolean(onEdit) && item.closable !== false && !item.disabled;
154
+ return /* @__PURE__ */ jsxs(
155
+ TabsTrigger,
156
+ {
157
+ value: item.value,
158
+ disabled: item.disabled,
159
+ "aria-keyshortcuts": removable ? "Delete" : void 0,
160
+ onKeyDown: removable ? (event) => {
161
+ if (event.key !== "Delete" && event.key !== "Backspace") return;
162
+ event.preventDefault();
163
+ onEdit?.(item.value, "remove");
164
+ } : void 0,
165
+ className: cn(
166
+ // Geometry only — the active underline is the token-owned `::after` bar the
167
+ // line variant already owns (never a second, hand-rolled border-b indicator).
168
+ // They are arbitrary-value utilities so tailwind-merge keeps dropping the pill
169
+ // trigger's own radius / padding steps from the base class list.
170
+ variant === "line" && "rounded-[var(--tabs-trigger-line-radius)] px-[var(--tabs-trigger-line-padding-x)] py-[var(--tabs-trigger-line-padding-y)]",
171
+ sizeTriggerClassName,
172
+ // A centred strip must let its triggers SHRINK to their labels first. The base
173
+ // trigger is `flex-1`, so on a full-width (`line`) strip they already fill every
174
+ // pixel and `justify-content` has nothing left to centre — measured, the leading and
175
+ // trailing gaps were both 0px with `centered` on and off.
176
+ centered && "flex-none",
177
+ // CARD face: a real boundary on every side, rounded on the leading block edge only.
178
+ // The ACTIVE face's block-end edge is the SURFACE colour, never `transparent`: the
179
+ // rail is an inset shadow at the strip's padding-box edge, so a see-through border
180
+ // would let that 1px of rail run straight across the tab it is joined to.
181
+ card && "data-[state=active]:border-b-background rounded-[var(--tabs-card-radius)_var(--tabs-card-radius)_0_0] border-[color:hsl(var(--border))] bg-[hsl(var(--tabs-card-background,var(--muted)))]"
182
+ ),
183
+ children: [
184
+ item.icon ? /* @__PURE__ */ jsx(
185
+ "span",
186
+ {
187
+ "data-slot": "tabs-trigger-icon",
188
+ className: "ui-tabs-trigger-icon",
189
+ "aria-hidden": "true",
190
+ children: item.icon
191
+ }
192
+ ) : null,
193
+ item.label,
194
+ removable ? (
195
+ // A POINTER SHORTCUT, not a control — and that is measured, not stylistic. antd's own
196
+ // TabNode puts a real <button> beside the role="tab" element; rendered through
197
+ // vitest-axe that markup fails `aria-required-children`, because a tablist may own
198
+ // nothing but tabs and axe walks straight through the generic wrapper to find the
199
+ // button. Putting the button INSIDE the tab is worse still (`nested-interactive`,
200
+ // plus a <button> in a <button> is invalid HTML). So the × is an aria-hidden span and
201
+ // the real, announced route is the Delete/Backspace shortcut above — the WAI-ARIA APG
202
+ // "tabs with deletion" shape.
203
+ /* @__PURE__ */ jsx(
204
+ "span",
205
+ {
206
+ "data-slot": "tabs-tab-remove",
207
+ className: "ui-tabs-tab-remove",
208
+ "aria-hidden": "true",
209
+ title: t("navigation.tabs.removeTab"),
210
+ onPointerDown: (event) => {
211
+ event.preventDefault();
212
+ event.stopPropagation();
213
+ },
214
+ onMouseDown: (event) => {
215
+ event.preventDefault();
216
+ event.stopPropagation();
217
+ },
218
+ onClick: (event) => {
219
+ event.preventDefault();
220
+ event.stopPropagation();
221
+ onEdit?.(item.value, "remove");
222
+ },
223
+ children: item.closeIcon ?? /* @__PURE__ */ jsx(X, { className: "ui-tabs-tab-remove-icon", "aria-hidden": "true" })
224
+ }
225
+ )
226
+ ) : null
227
+ ]
228
+ },
229
+ item.value
230
+ );
231
+ })
232
+ }
233
+ ) : null;
55
234
  return /* @__PURE__ */ jsx(TabsFrameContext.Provider, { value: frame, children: /* @__PURE__ */ jsx(
56
235
  AriaTabs,
57
236
  {
58
- orientation,
237
+ "data-slot": "tabs",
238
+ "data-orientation": resolvedOrientation,
239
+ "data-variant": variant,
240
+ "data-placement": placement,
241
+ "data-size": size,
242
+ "data-centered": centered ? "true" : void 0,
243
+ orientation: resolvedOrientation,
59
244
  keyboardActivation: activationMode,
60
245
  selectedKey: value,
61
246
  defaultSelectedKey: value === void 0 ? resolvedDefault : void 0,
62
- onSelectionChange: onValueChange ? (key) => {
63
- onValueChange(String(key));
64
- } : void 0,
247
+ onSelectionChange: (key) => handleValueChange(String(key)),
65
248
  className: cn(
66
- // Structure only.
67
- // live in src/styles/navigation-layout.css so the gap reads --tabs-root-gap and a service
249
+ // Structure only. The paint (and the placement flip, which is `order`/`flex-direction`)
250
+ // lives in src/styles/navigation-layout.css so the gap reads --tabs-root-gap and a service
251
+ // can retune it.
68
252
  "group/tabs flex data-[orientation=horizontal]:flex-col",
253
+ // PLACEMENT is a flex REVERSAL, never a re-ordered tree: the strip stays first in the DOM
254
+ // at every placement, so reading order and the APG tablist → tabpanel relationship do not
255
+ // depend on which edge the bar is painted on. Both classes carry the same modifier as the
256
+ // base step they replace, so tailwind-merge drops that step instead of stacking two
257
+ // flex-direction values on one element.
258
+ placement === "bottom" && "data-[orientation=horizontal]:flex-col-reverse",
259
+ placement === "end" && "flex-row-reverse",
69
260
  className
70
261
  ),
71
262
  render: (domProps) => withDomProps("div", "tabs", { dir, ...props }, domProps, {
72
- "data-orientation": orientation,
73
- "data-variant": variant
263
+ "data-orientation": resolvedOrientation,
264
+ "data-variant": variant,
265
+ "data-placement": placement,
266
+ "data-size": size,
267
+ "data-centered": centered ? "true" : void 0
74
268
  }),
75
269
  children: items ? /* @__PURE__ */ jsxs(Fragment, { children: [
76
- /* @__PURE__ */ jsx(
77
- TabsList,
78
- {
79
- "data-slot": "tabs-list",
80
- variant: variant === "line" ? "line" : "default",
81
- className: cn(
82
- // The inset override is an arbitrary value reading the knob (never `p-0`): it must
83
- // stay a UTILITY so tailwind-merge still drops the strip's own padding step.
84
- //
85
- // IT IS SPLIT INTO `px-`/`py-` FOR THE FOCUS RING (gh#376). The strip clips its
86
- // block axis (`overflow-y: hidden` beside `overflow-x: auto`), and this variant's
87
- // inset is 0px, so a focused trigger had NO block headroom at all — measured in
88
- // Chromium, 0px top and bottom, i.e. the entire ring was shaved. The headroom has to
89
- // come from layout: `overflow-y: visible` next to `overflow-x: auto` computes back
90
- // to `auto` per spec (a second scroll container, not a ring), and Chromium honours
91
- // `overflow-clip-margin` only when BOTH axes are `clip`. So the block padding carries
92
- // the ring's outer reach and an equal negative block margin hands it straight back —
93
- // the ring paints inside the scrollport and the strip's OUTER box is unchanged
94
- // (measured: 38px before and after). Both values read the same token the ring is
95
- // sized from, so the two can never drift apart.
96
- //
97
- // The cost, recorded rather than hidden: the rail hairline (`border-b`, drawn at the
98
- // border box) now sits the ring's reach below the triggers instead of flush. That is
99
- // where a hand-composed <TabsList variant="line"> already put it — its `p-1` gives
100
- // the same block inset — so the two construction paths now agree. A service that
101
- // wants the active bar parked back on the hairline raises `--tabs-indicator-offset`,
102
- // which exists for exactly that.
103
- variant === "line" && "my-[calc(-1_*_var(--tabs-list-focus-ring-space-inset,calc(var(--focus-ring-width)_+_var(--focus-ring-glow-width))))] h-auto w-full justify-start border-b px-[var(--tabs-list-line-space-inset)] py-[calc(var(--tabs-list-line-space-inset)_+_var(--tabs-list-focus-ring-space-inset,calc(var(--focus-ring-width)_+_var(--focus-ring-glow-width))))]",
104
- variant === "card" && "w-full justify-start",
105
- listClassName
106
- ),
107
- children: items.map((item) => /* @__PURE__ */ jsx(
108
- TabsTrigger,
109
- {
110
- value: item.value,
111
- disabled: item.disabled,
112
- className: cn(
113
- // Geometry only — the active underline is the token-owned `::after` bar the
114
- // line variant already owns (never a second, hand-rolled border-b indicator).
115
- // utilities so tailwind-merge keeps dropping the pill trigger's own radius /
116
- // padding steps from the base class list.
117
- // `card` adds NOTHING here — its list is forwarded as data-variant="default", so
118
- // the active lift already comes from the base trigger's
119
- // `group-data-[variant=default]/tabs-list:data-[state=active]:shadow-sm`.
120
- variant === "line" && "rounded-[var(--tabs-trigger-line-radius)] px-[var(--tabs-trigger-line-padding-x)] py-[var(--tabs-trigger-line-padding-y)]"
121
- ),
122
- children: item.label
123
- },
124
- item.value
125
- ))
126
- }
127
- ),
270
+ needsBar ? /* @__PURE__ */ jsxs("div", { "data-slot": "tabs-bar", className: "ui-tabs-bar", children: [
271
+ extraStart ? /* @__PURE__ */ jsx("div", { "data-slot": "tabs-extra", "data-side": "start", className: "ui-tabs-extra", children: extraStart }) : null,
272
+ list,
273
+ showAdd ? /* @__PURE__ */ jsx(
274
+ "button",
275
+ {
276
+ type: "button",
277
+ "data-slot": "tabs-add",
278
+ className: "ui-tabs-add",
279
+ "aria-label": t("navigation.tabs.addTab"),
280
+ onClick: (event) => onEdit?.(event, "add"),
281
+ children: addIcon ?? /* @__PURE__ */ jsx(Plus, { className: "ui-tabs-add-icon", "aria-hidden": "true" })
282
+ }
283
+ ) : null,
284
+ extraEnd ? /* @__PURE__ */ jsx("div", { "data-slot": "tabs-extra", "data-side": "end", className: "ui-tabs-extra", children: extraEnd }) : null
285
+ ] }) : list,
128
286
  items.map((item) => /* @__PURE__ */ jsx(
129
287
  TabsContent,
130
288
  {
131
289
  value: item.value,
132
290
  "data-slot": "tabs-panel",
291
+ forceMount: destroyOnHidden ? void 0 : true,
292
+ hidden: destroyOnHidden ? void 0 : item.value !== activeValue,
133
293
  className: contentClassName,
134
294
  children: item.content
135
295
  },
@@ -181,7 +341,7 @@ const TabsList = React.forwardRef(
181
341
  );
182
342
  TabsList.displayName = "TabsList";
183
343
  const TabsTrigger = React.forwardRef(
184
- ({ className, value, disabled, children, ...props }, ref) => {
344
+ ({ className, value, disabled, children, onKeyDown, ...props }, ref) => {
185
345
  const { orientation, selectionSuppressed } = React.useContext(TabsFrameContext);
186
346
  return /* @__PURE__ */ jsx(
187
347
  AriaTab,
@@ -219,6 +379,14 @@ const TabsTrigger = React.forwardRef(
219
379
  "aria-selected": renderProps.isSelected && !selectionSuppressed,
220
380
  "data-orientation": orientation,
221
381
  disabled,
382
+ onKeyDown: (event) => {
383
+ onKeyDown?.(event);
384
+ if (!event.defaultPrevented) {
385
+ domProps.onKeyDown?.(
386
+ event
387
+ );
388
+ }
389
+ },
222
390
  tabIndex: props.tabIndex ?? domProps.tabIndex
223
391
  }),
224
392
  children
@@ -1,5 +1,6 @@
1
1
  "use client";
2
2
  import { jsx } from "react/jsx-runtime";
3
+ import { useOverlayPortalContainer } from "../../lib/overlay-portal.js";
3
4
  import * as React from "react";
4
5
  import { chain, mergeRefs } from "@react-aria/utils";
5
6
  import { Popover as AriaPopover } from "react-aria-components";
@@ -142,9 +143,11 @@ function HoverCardContent({
142
143
  ...props
143
144
  }) {
144
145
  const card = useHoverCard("HoverCardContent");
146
+ const overlayPortalContainer = useOverlayPortalContainer();
145
147
  return /* @__PURE__ */ jsx(
146
148
  AriaPopover,
147
149
  {
150
+ UNSTABLE_portalContainer: overlayPortalContainer,
148
151
  isOpen: card.open,
149
152
  onOpenChange: card.setOpenImmediately,
150
153
  isNonModal: true,
@@ -1,5 +1,4 @@
1
1
  import * as React from "react";
2
- import type { InputOTPAlignProp } from "../../props/components/data-entry.prop.js";
3
2
  /**
4
3
  * InputOTP — the code-challenge field.
5
4
  *
@@ -8,39 +7,38 @@ import type { InputOTPAlignProp } from "../../props/components/data-entry.prop.j
8
7
  * hidden input because `input-otp` owns the container element; `.ui-otp-container` reads it back
9
8
  * through `:has()`, exactly as it already does for the invalid and disabled states. A service that
10
9
  * wants every code field centred sets `--otp-container-align` once instead.
10
+ *
11
+ * `size`/`status`/`variant` ride the same `controlSurfaceAttrs` helper as Input, Select, Cascader
12
+ * and TagInput, so the code field sits on the shared control-height ladder and paints the same
13
+ * error edge — they reach the CSS by the same `:has()` route as `align`.
14
+ *
15
+ * The VALUE is owned here, not by `input-otp`: `formatter` and `readOnly` have to hold for paste
16
+ * too, and `input-otp` writes its own internal state inside its paste handler, which a wrapper
17
+ * that only intercepted `onChange` could not undo.
11
18
  */
12
- export declare const InputOTP: React.ForwardRefExoticComponent<((Omit<Omit<React.InputHTMLAttributes<HTMLInputElement>, "containerClassName" | "maxLength" | "noScriptCSSFallback" | "nonce" | "onChange" | "onComplete" | "pasteTransformer" | "pushPasswordManagerStrategy" | "textAlign" | "value"> & {
13
- value?: string;
14
- onChange?: (newValue: string) => unknown;
19
+ export declare const InputOTP: React.ForwardRefExoticComponent<Omit<React.InputHTMLAttributes<HTMLInputElement>, "children" | "defaultValue" | "onChange" | "size" | "value"> & {
15
20
  maxLength: number;
16
- textAlign?: 'left' | 'center' | 'right';
17
- onComplete?: (...args: any[]) => unknown;
18
- pushPasswordManagerStrategy?: 'increase-width' | 'none';
19
- pasteTransformer?: (pasted: string) => string;
20
- containerClassName?: string;
21
- noScriptCSSFallback?: string | null;
22
- nonce?: string;
23
- } & {
24
- render: (props: import("input-otp").RenderProps) => React.ReactNode;
25
- children?: never;
26
- } & React.RefAttributes<HTMLInputElement>, "ref"> | Omit<Omit<React.InputHTMLAttributes<HTMLInputElement>, "containerClassName" | "maxLength" | "noScriptCSSFallback" | "nonce" | "onChange" | "onComplete" | "pasteTransformer" | "pushPasswordManagerStrategy" | "textAlign" | "value"> & {
27
21
  value?: string;
28
- onChange?: (newValue: string) => unknown;
29
- maxLength: number;
30
- textAlign?: 'left' | 'center' | 'right';
31
- onComplete?: (...args: any[]) => unknown;
32
- pushPasswordManagerStrategy?: 'increase-width' | 'none';
22
+ defaultValue?: string;
23
+ onValueChange?: (value: string) => void;
24
+ onChange?: (value: string) => void;
25
+ onComplete?: (value: string) => void;
26
+ formatter?: (value: string) => string;
27
+ mask?: import("./input-otp.js").InputOTPMaskProp;
28
+ align?: import("./input-otp.js").InputOTPAlignProp;
29
+ size?: import("../../props/index.js").SizeProp;
30
+ status?: import("../../props/index.js").ControlStatusProp;
31
+ variant?: import("../../props/index.js").ControlVariantProp;
32
+ pattern?: string;
33
33
  pasteTransformer?: (pasted: string) => string;
34
- containerClassName?: string;
34
+ containerClassName?: import("../../props/index.js").ClassNameProp;
35
+ pushPasswordManagerStrategy?: "increase-width" | "none";
35
36
  noScriptCSSFallback?: string | null;
36
37
  nonce?: string;
37
- } & {
38
- render?: never;
39
- children: React.ReactNode;
40
- } & React.RefAttributes<HTMLInputElement>, "ref">) & {
41
- align?: InputOTPAlignProp;
42
- }) & React.RefAttributes<HTMLInputElement>>;
43
- export type { InputOTPAlignProp, InputOTPGroupAppearanceProp, InputOTPGroupProp, InputOTPGroupProp as InputOTPGroupProps, } from "../../props/components/data-entry.prop.js";
38
+ children?: React.ReactNode;
39
+ render?: (props: import("input-otp").RenderProps) => React.ReactNode;
40
+ } & React.RefAttributes<HTMLInputElement>>;
41
+ export type { InputOTPAlignProp, InputOTPGroupAppearanceProp, InputOTPGroupProp, InputOTPGroupProp as InputOTPGroupProps, InputOTPMaskProp, InputOTPProp, InputOTPProp as InputOTPProps, } from "../../props/components/data-entry.prop.js";
44
42
  export declare const InputOTPGroup: React.ForwardRefExoticComponent<React.HTMLAttributes<HTMLDivElement> & {
45
43
  appearance?: import("./input-otp.js").InputOTPGroupAppearanceProp;
46
44
  } & React.RefAttributes<HTMLDivElement>>;
@@ -4,18 +4,58 @@ import * as React from "react";
4
4
  import { OTPInput, OTPInputContext } from "input-otp";
5
5
  import { Minus } from "lucide-react";
6
6
  import { cn } from "../../lib/utils.js";
7
- const InputOTP = React.forwardRef(({ className, containerClassName, align, ...props }, ref) => /* @__PURE__ */ jsx(
8
- OTPInput,
9
- {
7
+ import { controlSurfaceAttrs, resolveAriaInvalid } from "../data-entry/control-surface.js";
8
+ const DEFAULT_MASK_CHAR = "\u2022";
9
+ const InputOTPMaskContext = React.createContext(null);
10
+ const InputOTP = React.forwardRef(function InputOTP2({
11
+ className,
12
+ containerClassName,
13
+ align,
14
+ mask,
15
+ formatter,
16
+ size,
17
+ status,
18
+ variant,
19
+ value,
20
+ defaultValue,
21
+ onChange,
22
+ onValueChange,
23
+ readOnly,
24
+ "aria-invalid": ariaInvalid,
25
+ children,
26
+ render,
27
+ ...props
28
+ }, ref) {
29
+ const [uncontrolled, setUncontrolled] = React.useState(defaultValue ?? "");
30
+ const isControlled = value !== void 0;
31
+ const current = isControlled ? value : uncontrolled;
32
+ const handleChange = (next) => {
33
+ if (readOnly) return;
34
+ const formatted = formatter ? formatter(next) : next;
35
+ if (!isControlled) setUncontrolled(formatted);
36
+ onValueChange?.(formatted);
37
+ if (onChange !== onValueChange) onChange?.(formatted);
38
+ };
39
+ const maskChar = mask === true ? DEFAULT_MASK_CHAR : typeof mask === "string" && mask ? mask.charAt(0) : null;
40
+ const shared = {
10
41
  ref,
11
42
  "data-slot": "input-otp",
43
+ // INERT DEFAULT: `start` emits no attribute, so an existing field keeps its exact DOM.
12
44
  "data-align": align && align !== "start" ? align : void 0,
45
+ ...controlSurfaceAttrs({ size, status, variant }),
46
+ "aria-invalid": resolveAriaInvalid(ariaInvalid, status),
47
+ // Native `readonly` already conveys the state, but the library's readOnly contract names
48
+ // `aria-readonly` explicitly and half the controls did not honour it.
49
+ "aria-readonly": readOnly ? true : void 0,
50
+ readOnly,
51
+ value: current,
52
+ onChange: handleChange,
13
53
  containerClassName: cn("ui-otp-container", containerClassName),
14
54
  className: cn("ui-otp-input", className),
15
55
  ...props
16
- }
17
- ));
18
- InputOTP.displayName = "InputOTP";
56
+ };
57
+ return /* @__PURE__ */ jsx(InputOTPMaskContext.Provider, { value: maskChar, children: render ? /* @__PURE__ */ jsx(OTPInput, { ...shared, render }) : /* @__PURE__ */ jsx(OTPInput, { ...shared, children }) });
58
+ });
19
59
  const InputOTPGroup = React.forwardRef(
20
60
  ({ appearance = "slots", className, ...props }, ref) => /* @__PURE__ */ jsx(
21
61
  "div",
@@ -31,17 +71,20 @@ const InputOTPGroup = React.forwardRef(
31
71
  InputOTPGroup.displayName = "InputOTPGroup";
32
72
  const InputOTPSlot = React.forwardRef(({ index, className, ...props }, ref) => {
33
73
  const context = React.useContext(OTPInputContext);
74
+ const maskChar = React.useContext(InputOTPMaskContext);
34
75
  const slot = context.slots[index] ?? { char: null, hasFakeCaret: false, isActive: false };
76
+ const painted = slot.char === null ? null : maskChar ?? slot.char;
35
77
  return /* @__PURE__ */ jsxs(
36
78
  "div",
37
79
  {
38
80
  ref,
39
81
  "data-slot": "input-otp-slot",
40
82
  "data-active": slot.isActive || void 0,
83
+ "data-masked": maskChar && slot.char !== null ? "" : void 0,
41
84
  className: cn("ui-otp-slot", className),
42
85
  ...props,
43
86
  children: [
44
- slot.char,
87
+ painted,
45
88
  slot.hasFakeCaret ? /* @__PURE__ */ jsx("div", { className: "ui-otp-caret-wrapper", "aria-hidden": "true", children: /* @__PURE__ */ jsx("div", { className: "ui-otp-caret" }) }) : null
46
89
  ]
47
90
  }
@@ -1,4 +1,38 @@
1
1
  import * as React from "react";
2
2
  import { Input } from "../data-entry/input.js";
3
- export type PasswordInputProps = Omit<React.ComponentPropsWithoutRef<typeof Input>, "type">;
4
- export declare const PasswordInput: React.ForwardRefExoticComponent<PasswordInputProps & React.RefAttributes<HTMLInputElement>>;
3
+ /**
4
+ * antd `Input.Password.visibilityToggle`. `false` removes the eye entirely — a kiosk, a shared
5
+ * screen, or a security policy that forbids ever revealing a secret has no other way to say so.
6
+ * The object form is the controlled triad for the reveal state: `visible` wins when provided, and
7
+ * `onVisibleChange` fires on both the controlled and the uncontrolled path.
8
+ */
9
+ export type PasswordVisibilityToggleProp = boolean | {
10
+ visible?: boolean;
11
+ onVisibleChange?: (visible: boolean) => void;
12
+ };
13
+ export type PasswordInputProps = Omit<React.ComponentPropsWithoutRef<typeof Input>, "type"> & {
14
+ /** antd `visibilityToggle` — see {@link PasswordVisibilityToggleProp}. Default `true`. */
15
+ visibilityToggle?: PasswordVisibilityToggleProp;
16
+ /** antd `iconRender` — replace the eye glyph. Receives the CURRENT visibility. */
17
+ iconRender?: (visible: boolean) => React.ReactNode;
18
+ };
19
+ export declare const PasswordInput: React.ForwardRefExoticComponent<Omit<Omit<Omit<React.InputHTMLAttributes<HTMLInputElement>, "prefix" | "size"> & {
20
+ onValueChange?: (value: string) => void;
21
+ size?: "sm" | "md" | "lg";
22
+ status?: import("../../props/index.js").ControlStatusProp;
23
+ variant?: import("../../props/index.js").ControlVariantProp;
24
+ allowClear?: import("../../props/index.js").AllowClearProp;
25
+ onClear?: () => void;
26
+ leadingIcon?: React.ReactNode;
27
+ trailingIcon?: React.ReactNode;
28
+ prefix?: React.ReactNode;
29
+ suffix?: React.ReactNode;
30
+ addonBefore?: React.ReactNode;
31
+ addonAfter?: React.ReactNode;
32
+ count?: import("../../props/components/data-entry.prop.js").ControlCountProp;
33
+ } & React.RefAttributes<HTMLInputElement>, "ref">, "type"> & {
34
+ /** antd `visibilityToggle` — see {@link PasswordVisibilityToggleProp}. Default `true`. */
35
+ visibilityToggle?: PasswordVisibilityToggleProp;
36
+ /** antd `iconRender` — replace the eye glyph. Receives the CURRENT visibility. */
37
+ iconRender?: (visible: boolean) => React.ReactNode;
38
+ } & React.RefAttributes<HTMLInputElement>>;