@godxjp/ui 19.6.0 → 20.1.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 (326) hide show
  1. package/README.md +8 -2
  2. package/dist/app/theme-axes.d.ts +2 -0
  3. package/dist/app/theme-axes.js +45 -0
  4. package/dist/components/charts/chart-cartesian.d.ts +2 -1
  5. package/dist/components/charts/chart-cartesian.js +52 -3
  6. package/dist/components/charts/chart-category-axis.d.ts +55 -0
  7. package/dist/components/charts/chart-category-axis.js +93 -0
  8. package/dist/components/charts/chart-frame.d.ts +10 -1
  9. package/dist/components/charts/chart-frame.js +19 -3
  10. package/dist/components/charts/compact-bar-trend.d.ts +1 -1
  11. package/dist/components/charts/compact-bar-trend.js +2 -0
  12. package/dist/components/charts/pie-chart.d.ts +1 -1
  13. package/dist/components/charts/pie-chart.js +12 -1
  14. package/dist/components/charts/recharts-peer.d.ts +49 -0
  15. package/dist/components/charts/recharts-peer.js +45 -0
  16. package/dist/components/data-display/card.d.ts +14 -3
  17. package/dist/components/data-display/card.js +22 -4
  18. package/dist/components/data-display/code-block.js +6 -2
  19. package/dist/components/data-display/collapsible.d.ts +23 -4
  20. package/dist/components/data-display/collapsible.js +119 -4
  21. package/dist/components/data-display/data-table.d.ts +39 -4
  22. package/dist/components/data-display/data-table.js +802 -270
  23. package/dist/components/data-display/descriptions.d.ts +27 -6
  24. package/dist/components/data-display/descriptions.js +58 -17
  25. package/dist/components/data-display/index.d.ts +5 -1
  26. package/dist/components/data-display/index.js +4 -0
  27. package/dist/components/data-display/legend.d.ts +15 -0
  28. package/dist/components/data-display/legend.js +11 -0
  29. package/dist/components/data-display/list-row.d.ts +10 -1
  30. package/dist/components/data-display/list-row.js +8 -2
  31. package/dist/components/data-display/popover.d.ts +60 -7
  32. package/dist/components/data-display/popover.js +268 -39
  33. package/dist/components/data-display/progress.d.ts +50 -2
  34. package/dist/components/data-display/progress.js +57 -9
  35. package/dist/components/data-display/range-timeline.d.ts +38 -0
  36. package/dist/components/data-display/range-timeline.js +161 -0
  37. package/dist/components/data-display/scroll-area.js +13 -2
  38. package/dist/components/data-display/service-launcher-card.js +10 -10
  39. package/dist/components/data-display/table.d.ts +11 -3
  40. package/dist/components/data-display/table.js +48 -18
  41. package/dist/components/data-display/tree-list.js +4 -9
  42. package/dist/components/data-entry/calendar.d.ts +2 -2
  43. package/dist/components/data-entry/calendar.js +79 -33
  44. package/dist/components/data-entry/cascader.d.ts +1 -1
  45. package/dist/components/data-entry/cascader.js +188 -24
  46. package/dist/components/data-entry/checkbox.d.ts +19 -6
  47. package/dist/components/data-entry/checkbox.js +55 -16
  48. package/dist/components/data-entry/choice-option.d.ts +1 -1
  49. package/dist/components/data-entry/color-picker.d.ts +1 -1
  50. package/dist/components/data-entry/color-picker.js +17 -5
  51. package/dist/components/data-entry/control-appearance.d.ts +64 -0
  52. package/dist/components/data-entry/control-appearance.js +39 -0
  53. package/dist/components/data-entry/control-surface.d.ts +61 -0
  54. package/dist/components/data-entry/control-surface.js +39 -0
  55. package/dist/components/data-entry/date-picker.d.ts +1 -1
  56. package/dist/components/data-entry/date-picker.js +344 -113
  57. package/dist/components/data-entry/date-range-picker.d.ts +2 -2
  58. package/dist/components/data-entry/date-range-picker.js +278 -140
  59. package/dist/components/data-entry/field.js +0 -1
  60. package/dist/components/data-entry/form-field.d.ts +1 -1
  61. package/dist/components/data-entry/form-field.js +39 -4
  62. package/dist/components/data-entry/form.d.ts +4 -0
  63. package/dist/components/data-entry/form.js +5 -3
  64. package/dist/components/data-entry/index.d.ts +7 -3
  65. package/dist/components/data-entry/index.js +10 -1
  66. package/dist/components/data-entry/input-otp.d.ts +1 -1
  67. package/dist/components/data-entry/input.d.ts +12 -34
  68. package/dist/components/data-entry/input.js +92 -24
  69. package/dist/components/data-entry/label.d.ts +3 -2
  70. package/dist/components/data-entry/label.js +23 -10
  71. package/dist/components/data-entry/month-picker.d.ts +2 -2
  72. package/dist/components/data-entry/month-picker.js +47 -10
  73. package/dist/components/data-entry/month-range-picker.d.ts +2 -2
  74. package/dist/components/data-entry/month-range-picker.js +51 -11
  75. package/dist/components/data-entry/number-input.d.ts +7 -0
  76. package/dist/components/data-entry/number-input.js +147 -98
  77. package/dist/components/data-entry/password-input.d.ts +1 -1
  78. package/dist/components/data-entry/radio.d.ts +1 -1
  79. package/dist/components/data-entry/radio.js +61 -13
  80. package/dist/components/data-entry/search-input.d.ts +1 -1
  81. package/dist/components/data-entry/search-input.js +16 -2
  82. package/dist/components/data-entry/search-select.d.ts +2 -2
  83. package/dist/components/data-entry/search-select.js +209 -78
  84. package/dist/components/data-entry/select.d.ts +22 -4
  85. package/dist/components/data-entry/select.js +215 -163
  86. package/dist/components/data-entry/slider.d.ts +9 -1
  87. package/dist/components/data-entry/slider.js +87 -9
  88. package/dist/components/data-entry/switch.d.ts +3 -0
  89. package/dist/components/data-entry/switch.js +29 -3
  90. package/dist/components/data-entry/textarea.d.ts +14 -56
  91. package/dist/components/data-entry/textarea.js +80 -33
  92. package/dist/components/data-entry/time-picker.d.ts +2 -2
  93. package/dist/components/data-entry/time-picker.js +333 -109
  94. package/dist/components/data-entry/time-range-picker.d.ts +5 -0
  95. package/dist/components/data-entry/time-range-picker.js +89 -0
  96. package/dist/components/data-entry/transfer.d.ts +1 -1
  97. package/dist/components/data-entry/transfer.js +99 -31
  98. package/dist/components/data-entry/tree-select-strategy.d.ts +1 -1
  99. package/dist/components/data-entry/tree-select.d.ts +1 -1
  100. package/dist/components/data-entry/tree-select.js +197 -92
  101. package/dist/components/data-entry/tree-utils.d.ts +1 -1
  102. package/dist/components/data-entry/tree-utils.js +7 -14
  103. package/dist/components/data-entry/upload-files.d.ts +2 -0
  104. package/dist/components/data-entry/upload-files.js +31 -0
  105. package/dist/components/data-entry/upload-request.d.ts +4 -0
  106. package/dist/components/data-entry/upload-request.js +53 -0
  107. package/dist/components/data-entry/upload-types.d.ts +28 -0
  108. package/dist/components/data-entry/upload-types.js +2 -0
  109. package/dist/components/data-entry/upload.d.ts +2 -2
  110. package/dist/components/data-entry/upload.js +482 -117
  111. package/dist/components/feedback/dialog.d.ts +105 -38
  112. package/dist/components/feedback/dialog.js +268 -193
  113. package/dist/components/feedback/overlay-close-focus.d.ts +31 -0
  114. package/dist/components/feedback/overlay-close-focus.js +31 -0
  115. package/dist/components/feedback/overlay-header-tone.d.ts +1 -1
  116. package/dist/components/feedback/sheet.d.ts +53 -11
  117. package/dist/components/feedback/sheet.js +150 -81
  118. package/dist/components/feedback/tooltip.d.ts +51 -7
  119. package/dist/components/feedback/tooltip.js +107 -25
  120. package/dist/components/general/button.d.ts +1 -1
  121. package/dist/components/general/button.js +7 -3
  122. package/dist/components/general/index.d.ts +1 -0
  123. package/dist/components/general/index.js +2 -0
  124. package/dist/components/general/logo.d.ts +17 -0
  125. package/dist/components/general/logo.js +22 -16
  126. package/dist/components/general/typography.d.ts +3 -0
  127. package/dist/components/general/typography.js +15 -1
  128. package/dist/components/general/visually-hidden.d.ts +3 -0
  129. package/dist/components/general/visually-hidden.js +9 -0
  130. package/dist/components/layout/app-launcher.d.ts +34 -0
  131. package/dist/components/layout/app-launcher.js +228 -0
  132. package/dist/components/layout/app-shell.d.ts +3 -1
  133. package/dist/components/layout/app-shell.js +69 -12
  134. package/dist/components/layout/aspect-ratio.js +0 -1
  135. package/dist/components/layout/auth-divider.js +0 -1
  136. package/dist/components/layout/breadcrumb.d.ts +14 -2
  137. package/dist/components/layout/breadcrumb.js +46 -4
  138. package/dist/components/layout/flex.d.ts +1 -1
  139. package/dist/components/layout/flex.js +41 -3
  140. package/dist/components/layout/index.d.ts +3 -0
  141. package/dist/components/layout/index.js +5 -1
  142. package/dist/components/layout/nav-surface.d.ts +26 -0
  143. package/dist/components/layout/nav-surface.js +17 -0
  144. package/dist/components/layout/org-switcher.d.ts +5 -1
  145. package/dist/components/layout/org-switcher.js +25 -4
  146. package/dist/components/layout/page-container.d.ts +1 -1
  147. package/dist/components/layout/page-container.js +5 -3
  148. package/dist/components/layout/responsive-grid.d.ts +16 -2
  149. package/dist/components/layout/responsive-grid.js +29 -2
  150. package/dist/components/layout/separator.js +0 -1
  151. package/dist/components/layout/sidebar.js +4 -1
  152. package/dist/components/layout/split-pane.d.ts +14 -1
  153. package/dist/components/layout/split-pane.js +26 -13
  154. package/dist/components/layout/topbar-item.d.ts +3 -0
  155. package/dist/components/layout/topbar-item.js +19 -5
  156. package/dist/components/layout/topbar.d.ts +1 -1
  157. package/dist/components/layout/topbar.js +27 -6
  158. package/dist/components/navigation/app-setting-picker.js +21 -2
  159. package/dist/components/navigation/app-setting-toggle.d.ts +16 -0
  160. package/dist/components/navigation/app-setting-toggle.js +96 -0
  161. package/dist/components/navigation/dropdown-menu.d.ts +227 -18
  162. package/dist/components/navigation/dropdown-menu.js +402 -118
  163. package/dist/components/navigation/index.d.ts +2 -0
  164. package/dist/components/navigation/index.js +2 -0
  165. package/dist/components/navigation/pagination-utils.d.ts +2 -1
  166. package/dist/components/navigation/pagination.d.ts +2 -2
  167. package/dist/components/navigation/pagination.js +155 -83
  168. package/dist/components/navigation/steps.d.ts +2 -2
  169. package/dist/components/navigation/steps.js +29 -4
  170. package/dist/components/navigation/tabs.d.ts +29 -16
  171. package/dist/components/navigation/tabs.js +374 -144
  172. package/dist/components/ui/accordion.d.ts +50 -5
  173. package/dist/components/ui/accordion.js +239 -33
  174. package/dist/components/ui/aspect-ratio.d.ts +23 -2
  175. package/dist/components/ui/aspect-ratio.js +15 -13
  176. package/dist/components/ui/avatar.d.ts +29 -4
  177. package/dist/components/ui/avatar.js +111 -25
  178. package/dist/components/ui/hover-card.d.ts +42 -4
  179. package/dist/components/ui/hover-card.js +183 -27
  180. package/dist/components/ui/input-otp.d.ts +26 -28
  181. package/dist/components/ui/input-otp.js +50 -7
  182. package/dist/components/ui/label.js +0 -1
  183. package/dist/components/ui/password-input.d.ts +36 -2
  184. package/dist/components/ui/password-input.js +27 -7
  185. package/dist/components/ui/rating.d.ts +25 -0
  186. package/dist/components/ui/rating.js +67 -27
  187. package/dist/components/ui/segmented.d.ts +12 -3
  188. package/dist/components/ui/segmented.js +17 -2
  189. package/dist/components/ui/separator.d.ts +12 -2
  190. package/dist/components/ui/separator.js +30 -9
  191. package/dist/components/ui/tag-input.d.ts +45 -0
  192. package/dist/components/ui/tag-input.js +109 -27
  193. package/dist/components/ui/toggle-group.d.ts +50 -5
  194. package/dist/components/ui/toggle-group.js +79 -20
  195. package/dist/components/ui/toggle.d.ts +31 -5
  196. package/dist/components/ui/toggle.js +42 -3
  197. package/dist/form/form-context.d.ts +29 -0
  198. package/dist/form/form-context.js +44 -4
  199. package/dist/form/form-field-array.d.ts +22 -0
  200. package/dist/form/form-field-array.js +50 -0
  201. package/dist/form/form-field-control.d.ts +2 -1
  202. package/dist/form/form-field-control.js +116 -40
  203. package/dist/form/form-root.d.ts +2 -1
  204. package/dist/form/form-root.js +142 -23
  205. package/dist/form/index.d.ts +3 -1
  206. package/dist/form/index.js +12 -1
  207. package/dist/i18n/messages/en.json +64 -5
  208. package/dist/i18n/messages/ja.json +64 -5
  209. package/dist/i18n/messages/vi.json +64 -5
  210. package/dist/inertia/index.d.ts +20 -0
  211. package/dist/inertia/index.js +60 -1
  212. package/dist/lib/control-styles.d.ts +25 -3
  213. package/dist/lib/control-styles.js +9 -3
  214. package/dist/lib/datetime/picker-format.d.ts +8 -0
  215. package/dist/lib/datetime/picker-format.js +54 -0
  216. package/dist/lib/slot.d.ts +32 -0
  217. package/dist/lib/slot.js +22 -0
  218. package/dist/lib/variants.d.ts +22 -3
  219. package/dist/lib/variants.js +56 -1
  220. package/dist/props/components/app.prop.d.ts +25 -1
  221. package/dist/props/components/charts.prop.d.ts +21 -0
  222. package/dist/props/components/data-display.prop.d.ts +72 -4
  223. package/dist/props/components/data-entry.prop.d.ts +718 -46
  224. package/dist/props/components/form.prop.d.ts +142 -4
  225. package/dist/props/components/general.prop.d.ts +17 -1
  226. package/dist/props/components/index.d.ts +3 -3
  227. package/dist/props/components/layout.prop.d.ts +281 -11
  228. package/dist/props/components/navigation.prop.d.ts +114 -4
  229. package/dist/props/registry.d.ts +397 -11
  230. package/dist/props/registry.js +526 -11
  231. package/dist/props/vocabulary/content.prop.d.ts +1 -1
  232. package/dist/props/vocabulary/data.prop.d.ts +179 -1
  233. package/dist/props/vocabulary/index.d.ts +5 -5
  234. package/dist/props/vocabulary/interaction.prop.d.ts +68 -3
  235. package/dist/props/vocabulary/layout.prop.d.ts +57 -1
  236. package/dist/props/vocabulary/navigation.prop.d.ts +40 -0
  237. package/dist/props/vocabulary/shared.prop.d.ts +33 -0
  238. package/dist/styles/badge-layout.css +2 -1
  239. package/dist/styles/card-layout.css +26 -4
  240. package/dist/styles/chart-layout.css +15 -0
  241. package/dist/styles/control.css +657 -15
  242. package/dist/styles/core.css +5 -2
  243. package/dist/styles/data-display-layout.css +313 -6
  244. package/dist/styles/data-entry-layout.css +13 -0
  245. package/dist/styles/focus-ring.css +4 -0
  246. package/dist/styles/index.css +5 -2
  247. package/dist/styles/layout.css +327 -4
  248. package/dist/styles/navigation-layout.css +227 -0
  249. package/dist/styles/shell-layout.css +369 -13
  250. package/dist/styles/table-layout.css +95 -6
  251. package/dist/styles/text-layout.css +10 -4
  252. package/dist/tokens/base.css +2 -1
  253. package/dist/tokens/components/badge.css +1 -0
  254. package/dist/tokens/components/chart.css +6 -0
  255. package/dist/tokens/components/control.css +79 -2
  256. package/dist/tokens/components/data-display.css +20 -0
  257. package/dist/tokens/components/descriptions.css +11 -0
  258. package/dist/tokens/components/flex.css +8 -0
  259. package/dist/tokens/components/navigation.css +55 -0
  260. package/dist/tokens/components/shell.css +53 -3
  261. package/dist/tokens/components/table.css +14 -0
  262. package/dist/tokens/foundation.css +2 -0
  263. package/dist/tokens/semantic/layout.css +9 -0
  264. package/docs/COMPONENTS.md +9 -3
  265. package/docs/CONSUMER-RULES.md +13 -0
  266. package/docs/DESIGN-AUTHORITY.md +145 -83
  267. package/docs/DEVELOPMENT.md +4 -3
  268. package/docs/FORMS.md +151 -6
  269. package/docs/FRAME-COVERAGE-REPORT.md +32 -17
  270. package/docs/README.md +15 -15
  271. package/docs/STANDARDS-vocabulary-tokens.md +1 -1
  272. package/docs/TESTING.md +15 -6
  273. package/docs/WHAT-BELONGS-HERE.md +179 -0
  274. package/docs/charts/cjk-category-axis.tsx +81 -0
  275. package/docs/data-display/card/index.tsx +22 -0
  276. package/docs/data-display/data-table/examples/antd-parity.tsx +257 -0
  277. package/docs/data-display/data-table/index.tsx +23 -0
  278. package/docs/data-display/descriptions.tsx +41 -0
  279. package/docs/data-display/legend.tsx +145 -0
  280. package/docs/data-display/progress.tsx +32 -0
  281. package/docs/data-display/scroll-area.tsx +31 -25
  282. package/docs/data-display/table.tsx +104 -45
  283. package/docs/data-display/timeline.tsx +41 -0
  284. package/docs/data-entry/date-picker.tsx +85 -0
  285. package/docs/data-entry/date-range-picker.tsx +26 -0
  286. package/docs/data-entry/form-dynamic-fields.tsx +199 -0
  287. package/docs/data-entry/form.tsx +74 -4
  288. package/docs/data-entry/input-otp.tsx +24 -0
  289. package/docs/data-entry/input.tsx +46 -1
  290. package/docs/data-entry/month-range-picker.tsx +1 -1
  291. package/docs/data-entry/segmented.tsx +1 -1
  292. package/docs/data-entry/select.tsx +29 -2
  293. package/docs/data-entry/switch.tsx +41 -0
  294. package/docs/data-entry/textarea.tsx +38 -0
  295. package/docs/data-entry/time-picker.tsx +85 -1
  296. package/docs/data-entry/time-range-picker.tsx +55 -0
  297. package/docs/data-entry/transfer.tsx +17 -0
  298. package/docs/data-entry/upload.tsx +56 -12
  299. package/docs/feedback/sheet.tsx +1 -1
  300. package/docs/feedback/tooltip.tsx +1 -1
  301. package/docs/general/activity.tsx +2 -2
  302. package/docs/general/button/index.tsx +14 -1
  303. package/docs/general/typography.tsx +14 -1
  304. package/docs/layout/app-launcher.tsx +151 -0
  305. package/docs/layout/app-shell-arrangements.tsx +225 -0
  306. package/docs/layout/app-shell.tsx +11 -0
  307. package/docs/layout/aspect-ratio.tsx +1 -1
  308. package/docs/layout/flex.tsx +50 -0
  309. package/docs/layout/responsive-grid.tsx +21 -1
  310. package/docs/layout/topbar.tsx +5 -9
  311. package/docs/navigation/app-setting-picker.tsx +37 -1
  312. package/docs/navigation/app-setting-toggle.tsx +111 -0
  313. package/docs/navigation/breadcrumb.tsx +48 -0
  314. package/docs/navigation/dropdown-menu.tsx +21 -0
  315. package/docs/navigation/pagination.tsx +52 -0
  316. package/docs/navigation/steps.tsx +37 -0
  317. package/docs/navigation/tabs.tsx +97 -1
  318. package/docs/query/button-refetch.tsx +1 -0
  319. package/package.json +28 -8
  320. package/scripts/_agent-setup.mjs +182 -3
  321. package/scripts/consumer-rule.md +98 -0
  322. package/scripts/guinea-pig-skill.md +322 -0
  323. package/scripts/init-guinea-pig.mjs +84 -0
  324. package/scripts/postinstall.mjs +13 -2
  325. package/scripts/ui-audit.mjs +351 -37
  326. /package/dist/tokens/{antd.generated.css → derived.css} +0 -0
@@ -1,44 +1,200 @@
1
1
  "use client";
2
2
  import { jsx } from "react/jsx-runtime";
3
3
  import * as React from "react";
4
- import * as HoverCardPrimitive from "@radix-ui/react-hover-card";
4
+ import { chain, mergeRefs } from "@react-aria/utils";
5
+ import { Popover as AriaPopover } from "react-aria-components";
5
6
  import { cn } from "../../lib/utils.js";
7
+ import { Slot } from "../../lib/slot.js";
8
+ function toPlacement(side, align) {
9
+ if (align === "center") return side;
10
+ if (side === "top" || side === "bottom") return `${side} ${align}`;
11
+ return `${side} ${align === "start" ? "top" : "bottom"}`;
12
+ }
13
+ function toContainerPadding(padding) {
14
+ if (padding == null) return void 0;
15
+ if (typeof padding === "number") return padding;
16
+ const sides = Object.values(padding).filter(
17
+ (value) => typeof value === "number"
18
+ );
19
+ return sides.length ? Math.max(...sides) : void 0;
20
+ }
21
+ const HoverCardContext = React.createContext(null);
22
+ function useHoverCard(component) {
23
+ const context = React.useContext(HoverCardContext);
24
+ if (!context) {
25
+ throw new Error(`\`${component}\` ph\u1EA3i n\u1EB1m trong \`HoverCard\`.`);
26
+ }
27
+ return context;
28
+ }
6
29
  function HoverCard({
30
+ open,
31
+ defaultOpen,
32
+ onOpenChange,
7
33
  openDelay = 200,
8
34
  closeDelay = 100,
35
+ children
36
+ }) {
37
+ const [uncontrolledOpen, setUncontrolledOpen] = React.useState(defaultOpen ?? false);
38
+ const triggerRef = React.useRef(null);
39
+ const openTimer = React.useRef(null);
40
+ const closeTimer = React.useRef(null);
41
+ const isOpen = open ?? uncontrolledOpen;
42
+ const setOpenImmediately = React.useCallback(
43
+ (next) => {
44
+ if (open === void 0) {
45
+ setUncontrolledOpen(next);
46
+ }
47
+ onOpenChange?.(next);
48
+ },
49
+ [open, onOpenChange]
50
+ );
51
+ const value = React.useMemo(() => {
52
+ const clearOpen = () => {
53
+ if (openTimer.current) clearTimeout(openTimer.current);
54
+ openTimer.current = null;
55
+ };
56
+ const clearClose = () => {
57
+ if (closeTimer.current) clearTimeout(closeTimer.current);
58
+ closeTimer.current = null;
59
+ };
60
+ return {
61
+ open: isOpen,
62
+ triggerRef,
63
+ setOpenImmediately: (next) => {
64
+ clearOpen();
65
+ clearClose();
66
+ setOpenImmediately(next);
67
+ },
68
+ scheduleOpen: () => {
69
+ clearClose();
70
+ if (openTimer.current) return;
71
+ openTimer.current = setTimeout(() => {
72
+ openTimer.current = null;
73
+ setOpenImmediately(true);
74
+ }, openDelay);
75
+ },
76
+ scheduleClose: () => {
77
+ clearOpen();
78
+ if (closeTimer.current) return;
79
+ closeTimer.current = setTimeout(() => {
80
+ closeTimer.current = null;
81
+ setOpenImmediately(false);
82
+ }, closeDelay);
83
+ },
84
+ cancelClose: clearClose
85
+ };
86
+ }, [isOpen, setOpenImmediately, openDelay, closeDelay]);
87
+ React.useEffect(
88
+ () => () => {
89
+ if (openTimer.current) clearTimeout(openTimer.current);
90
+ if (closeTimer.current) clearTimeout(closeTimer.current);
91
+ },
92
+ []
93
+ );
94
+ return /* @__PURE__ */ jsx(HoverCardContext.Provider, { value, children });
95
+ }
96
+ function HoverCardTrigger({
97
+ asChild,
98
+ onPointerEnter,
99
+ onPointerLeave,
100
+ onFocus,
101
+ onBlur,
102
+ ref,
9
103
  ...props
10
104
  }) {
105
+ const card = useHoverCard("HoverCardTrigger");
106
+ const Comp = asChild ? Slot : "a";
11
107
  return /* @__PURE__ */ jsx(
12
- HoverCardPrimitive.Root,
108
+ Comp,
13
109
  {
14
- "data-slot": "hover-card",
15
- openDelay,
16
- closeDelay,
17
- ...props
110
+ "data-state": card.open ? "open" : "closed",
111
+ ...props,
112
+ ref: mergeRefs(ref, card.triggerRef),
113
+ onPointerEnter: chain(onPointerEnter, (event) => {
114
+ if (event.pointerType === "touch") return;
115
+ card.scheduleOpen();
116
+ }),
117
+ onPointerLeave: chain(onPointerLeave, (event) => {
118
+ if (event.pointerType === "touch") return;
119
+ card.scheduleClose();
120
+ }),
121
+ onFocus: chain(onFocus, () => card.setOpenImmediately(true)),
122
+ onBlur: chain(onBlur, () => card.setOpenImmediately(false))
123
+ }
124
+ );
125
+ }
126
+ function HoverCardContent({
127
+ className,
128
+ style,
129
+ children,
130
+ ref,
131
+ side = "bottom",
132
+ align = "center",
133
+ sideOffset = 4,
134
+ alignOffset,
135
+ avoidCollisions = true,
136
+ collisionPadding,
137
+ sticky: _sticky,
138
+ hideWhenDetached: _hideWhenDetached,
139
+ forceMount: _forceMount,
140
+ onPointerEnter,
141
+ onPointerLeave,
142
+ ...props
143
+ }) {
144
+ const card = useHoverCard("HoverCardContent");
145
+ return /* @__PURE__ */ jsx(
146
+ AriaPopover,
147
+ {
148
+ isOpen: card.open,
149
+ onOpenChange: card.setOpenImmediately,
150
+ isNonModal: true,
151
+ triggerRef: card.triggerRef,
152
+ placement: toPlacement(side, align),
153
+ offset: sideOffset,
154
+ crossOffset: alignOffset,
155
+ shouldFlip: avoidCollisions,
156
+ containerPadding: toContainerPadding(collisionPadding),
157
+ render: (racProps, { isExiting }) => {
158
+ const {
159
+ className: _racClassName,
160
+ "data-rac": _rac,
161
+ ref: racRef,
162
+ style: racStyle,
163
+ ...rest
164
+ } = racProps;
165
+ return /* @__PURE__ */ jsx(
166
+ "div",
167
+ {
168
+ ...rest,
169
+ "data-slot": "hover-card-content",
170
+ "data-side": side,
171
+ "data-align": align,
172
+ "data-state": isExiting ? "closed" : "open",
173
+ ...props,
174
+ ref: mergeRefs(ref, racRef),
175
+ onPointerEnter: chain(onPointerEnter, () => card.cancelClose()),
176
+ onPointerLeave: chain(onPointerLeave, () => card.scheduleClose()),
177
+ className: cn(
178
+ // Match Popover/Tooltip: a directional fade+zoom+slide entrance so it doesn't pop abruptly.
179
+ "ui-hover-card-content origin-[var(--radix-hover-card-content-transform-origin)]",
180
+ "data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
181
+ "data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95",
182
+ "data-[side=bottom]:slide-in-from-top-2 data-[side=top]:slide-in-from-bottom-2",
183
+ "data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2",
184
+ className
185
+ ),
186
+ style: {
187
+ ...racStyle,
188
+ "--radix-hover-card-content-transform-origin": "var(--trigger-anchor-point)",
189
+ ...style
190
+ },
191
+ children
192
+ }
193
+ );
194
+ }
18
195
  }
19
196
  );
20
197
  }
21
- const HoverCardTrigger = HoverCardPrimitive.Trigger;
22
- const HoverCardContent = React.forwardRef(({ className, align = "center", sideOffset = 4, ...props }, ref) => /* @__PURE__ */ jsx(HoverCardPrimitive.Portal, { children: /* @__PURE__ */ jsx(
23
- HoverCardPrimitive.Content,
24
- {
25
- ref,
26
- "data-slot": "hover-card-content",
27
- align,
28
- sideOffset,
29
- className: cn(
30
- // Match Popover/Tooltip: a directional fade+zoom+slide entrance so it doesn't pop abruptly.
31
- "ui-hover-card-content origin-[var(--radix-hover-card-content-transform-origin)]",
32
- "data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
33
- "data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95",
34
- "data-[side=bottom]:slide-in-from-top-2 data-[side=top]:slide-in-from-bottom-2",
35
- "data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2",
36
- className
37
- ),
38
- ...props
39
- }
40
- ) }));
41
- HoverCardContent.displayName = HoverCardPrimitive.Content.displayName;
42
198
  export {
43
199
  HoverCard,
44
200
  HoverCardContent,
@@ -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,2 +1 @@
1
- "use client";
2
1
  export * from "../data-entry/label.js";
@@ -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>>;
@@ -6,30 +6,50 @@ import { useTranslation } from "../../i18n/use-translation.js";
6
6
  import { cn } from "../../lib/utils.js";
7
7
  import { Input } from "../data-entry/input.js";
8
8
  const PasswordInput = React.forwardRef(
9
- ({ className, ...props }, ref) => {
9
+ ({ className, visibilityToggle = true, iconRender, suffix, trailingIcon, allowClear, ...props }, ref) => {
10
10
  const { t } = useTranslation();
11
- const [visible, setVisible] = React.useState(false);
11
+ const controlledVisible = typeof visibilityToggle === "object" ? visibilityToggle.visible : void 0;
12
+ const onVisibleChange = typeof visibilityToggle === "object" ? visibilityToggle.onVisibleChange : void 0;
13
+ const showToggle = visibilityToggle !== false;
14
+ const [internalVisible, setInternalVisible] = React.useState(false);
15
+ const isVisibleControlled = controlledVisible !== void 0;
16
+ const visible = showToggle ? isVisibleControlled ? controlledVisible : internalVisible : false;
17
+ const setVisible = (next) => {
18
+ if (!isVisibleControlled) setInternalVisible(next);
19
+ onVisibleChange?.(next);
20
+ };
21
+ const toggleOwnsTrailing = showToggle;
22
+ if (toggleOwnsTrailing && process.env?.NODE_ENV !== "production" && (suffix != null || trailingIcon != null || allowClear)) {
23
+ console.warn(
24
+ "[godxjp-ui] PasswordInput: the reveal toggle owns the trailing slot, so `suffix` / `trailingIcon` / `allowClear` are ignored. Use `leadingIcon` (or `prefix`), or pass `visibilityToggle={false}` to hand the slot back."
25
+ );
26
+ }
12
27
  return /* @__PURE__ */ jsxs("div", { className: "ui-password-input", "data-slot": "password-input", children: [
13
28
  /* @__PURE__ */ jsx(
14
29
  Input,
15
30
  {
16
31
  ref,
17
32
  type: visible ? "text" : "password",
18
- className: cn("ui-password-input-field", className),
33
+ className: cn(showToggle && "ui-password-input-field", className),
34
+ suffix: toggleOwnsTrailing ? void 0 : suffix,
35
+ trailingIcon: toggleOwnsTrailing ? void 0 : trailingIcon,
36
+ allowClear: toggleOwnsTrailing ? false : allowClear,
19
37
  ...props
20
38
  }
21
39
  ),
22
- /* @__PURE__ */ jsx(
40
+ showToggle ? /* @__PURE__ */ jsx(
23
41
  "button",
24
42
  {
25
43
  type: "button",
26
44
  className: "ui-password-input-toggle",
27
- onClick: () => setVisible((v) => !v),
45
+ disabled: props.disabled,
46
+ onMouseDown: (event) => event.preventDefault(),
47
+ onClick: () => setVisible(!visible),
28
48
  "aria-label": visible ? t("ui.passwordInput.hide") : t("ui.passwordInput.show"),
29
49
  "aria-pressed": visible,
30
- children: visible ? /* @__PURE__ */ jsx(EyeOff, { "aria-hidden": "true" }) : /* @__PURE__ */ jsx(Eye, { "aria-hidden": "true" })
50
+ children: iconRender ? iconRender(visible) : visible ? /* @__PURE__ */ jsx(EyeOff, { "aria-hidden": "true" }) : /* @__PURE__ */ jsx(Eye, { "aria-hidden": "true" })
31
51
  }
32
- )
52
+ ) : null
33
53
  ] });
34
54
  }
35
55
  );
@@ -3,7 +3,32 @@ export type RatingProps = {
3
3
  value?: number;
4
4
  defaultValue?: number;
5
5
  onValueChange?: (value: number) => void;
6
+ /** Number of symbols. This library's older name for antd's `count`; both are accepted. */
6
7
  max?: number;
8
+ /** antd `count` — number of symbols. Default 5. */
9
+ count?: number;
10
+ /**
11
+ * antd `allowHalf` — a symbol can be half-filled, so the scale is 0.5 … count in steps of 0.5.
12
+ * The keyboard steps by a half too, and each symbol becomes TWO hit areas (leading / trailing).
13
+ */
14
+ allowHalf?: boolean;
15
+ /**
16
+ * antd `allowClear` — choosing the value that is already chosen clears it back to 0. On by
17
+ * default in antd; off here, because a rating inside a form is usually required and a silent
18
+ * reset on a second click reads as a lost answer.
19
+ */
20
+ allowClear?: boolean;
21
+ /**
22
+ * antd `character` — what a symbol IS. A node for every symbol, or a function of the 1-based
23
+ * index for a scale whose symbols differ (A/B/C, 松竹梅, a heart for the top step).
24
+ */
25
+ character?: React.ReactNode | ((index: number) => React.ReactNode);
26
+ /**
27
+ * antd `tooltips` — a label per step, in order. It is folded into each symbol's ACCESSIBLE NAME
28
+ * rather than shown only on hover: a `title` attribute is invisible to a keyboard and to touch,
29
+ * and the whole point of the prop is to say what "3 of 5" means.
30
+ */
31
+ tooltips?: readonly string[];
7
32
  readOnly?: boolean;
8
33
  disabled?: boolean;
9
34
  name?: string;
@@ -9,7 +9,12 @@ const Rating = React.forwardRef(
9
9
  value,
10
10
  defaultValue = 0,
11
11
  onValueChange,
12
- max = 5,
12
+ max,
13
+ count,
14
+ allowHalf = false,
15
+ allowClear = false,
16
+ character,
17
+ tooltips,
13
18
  readOnly,
14
19
  disabled,
15
20
  name,
@@ -17,68 +22,103 @@ const Rating = React.forwardRef(
17
22
  ...rest
18
23
  }, ref) => {
19
24
  const { t } = useTranslation();
25
+ const symbols = count ?? max ?? 5;
20
26
  const [internal, setInternal] = React.useState(defaultValue);
21
27
  const current = value ?? internal;
22
28
  const [hover, setHover] = React.useState(null);
23
29
  const display = hover ?? current;
24
30
  const interactive = !readOnly && !disabled;
31
+ const stepSize = allowHalf ? 0.5 : 1;
25
32
  const select = (next) => {
26
33
  if (!interactive) return;
27
- if (value === void 0) setInternal(next);
28
- onValueChange?.(next);
34
+ const resolved = allowClear && next === current ? 0 : next;
35
+ if (value === void 0) setInternal(resolved);
36
+ onValueChange?.(resolved);
29
37
  };
30
- const focusableStar = current > 0 ? current : 1;
38
+ const focusableStar = current > 0 ? Math.ceil(current) : 1;
31
39
  const onKeyDown = (e, star) => {
32
40
  if (!interactive) return;
33
41
  let next;
34
42
  switch (e.key) {
35
43
  case "ArrowRight":
36
44
  case "ArrowUp":
37
- next = Math.min(max, star + 1);
45
+ next = Math.min(symbols, star + stepSize);
38
46
  break;
39
47
  case "ArrowLeft":
40
48
  case "ArrowDown":
41
- next = Math.max(1, star - 1);
49
+ next = Math.max(stepSize, star - stepSize);
42
50
  break;
43
51
  case "Home":
44
- next = 1;
52
+ next = stepSize;
45
53
  break;
46
54
  case "End":
47
- next = max;
55
+ next = symbols;
48
56
  break;
49
57
  default:
50
58
  return;
51
59
  }
52
60
  e.preventDefault();
53
- select(next);
61
+ if (value === void 0) setInternal(next);
62
+ onValueChange?.(next);
63
+ };
64
+ const stepLabel = (star) => {
65
+ const tooltip = tooltips?.[star - 1];
66
+ const base = t("ui.rating.starLabel", { star, max: symbols });
67
+ return tooltip ? `${base}, ${tooltip}` : base;
54
68
  };
69
+ const glyph = (star) => typeof character === "function" ? character(star) : character ?? /* @__PURE__ */ jsx(Star, { "aria-hidden": "true" });
55
70
  return /* @__PURE__ */ jsxs(
56
71
  "div",
57
72
  {
58
73
  ref,
59
74
  role: "radiogroup",
60
75
  "data-slot": "rating",
76
+ "data-allow-half": allowHalf ? "true" : void 0,
61
77
  className: cn("ui-rating", className),
62
78
  "aria-label": rest["aria-label"] ?? t("ui.rating.ariaLabel"),
63
79
  children: [
64
- Array.from({ length: max }, (_, i) => i + 1).map((star) => /* @__PURE__ */ jsx(
65
- "button",
66
- {
67
- type: "button",
68
- role: "radio",
69
- "aria-checked": current === star,
70
- "aria-label": t("ui.rating.starLabel", { star, max }),
71
- disabled: disabled || readOnly,
72
- tabIndex: interactive && star === focusableStar ? 0 : -1,
73
- className: cn("ui-rating-star", star <= display && "ui-rating-star-filled"),
74
- onMouseEnter: () => interactive && setHover(star),
75
- onMouseLeave: () => setHover(null),
76
- onClick: () => select(star),
77
- onKeyDown: (e) => onKeyDown(e, star),
78
- children: /* @__PURE__ */ jsx(Star, { "aria-hidden": "true" })
79
- },
80
- star
81
- )),
80
+ Array.from({ length: symbols }, (_, i) => i + 1).map((star) => {
81
+ const symbol = /* @__PURE__ */ jsx(
82
+ "button",
83
+ {
84
+ type: "button",
85
+ role: "radio",
86
+ "aria-checked": current === star,
87
+ "aria-label": stepLabel(star),
88
+ disabled: disabled || readOnly,
89
+ tabIndex: interactive && star === focusableStar ? 0 : -1,
90
+ className: cn(
91
+ "ui-rating-star",
92
+ star <= display && "ui-rating-star-filled",
93
+ allowHalf && star - 0.5 === display && "ui-rating-star-half-filled"
94
+ ),
95
+ onMouseEnter: () => interactive && setHover(star),
96
+ onMouseLeave: () => setHover(null),
97
+ onClick: () => select(star),
98
+ onKeyDown: (e) => onKeyDown(e, star),
99
+ children: glyph(star)
100
+ },
101
+ star
102
+ );
103
+ if (!allowHalf) return symbol;
104
+ return /* @__PURE__ */ jsxs("span", { "data-slot": "rating-symbol", className: "ui-rating-symbol", children: [
105
+ /* @__PURE__ */ jsx(
106
+ "button",
107
+ {
108
+ type: "button",
109
+ tabIndex: -1,
110
+ "aria-hidden": "true",
111
+ disabled: disabled || readOnly,
112
+ "data-slot": "rating-half",
113
+ className: "ui-rating-star-half",
114
+ onMouseEnter: () => interactive && setHover(star - 0.5),
115
+ onMouseLeave: () => setHover(null),
116
+ onClick: () => select(star - 0.5)
117
+ }
118
+ ),
119
+ symbol
120
+ ] }, star);
121
+ }),
82
122
  name ? /* @__PURE__ */ jsx("input", { type: "hidden", name, value: current }) : null
83
123
  ]
84
124
  }