@inspirare/design-system 0.0.22 → 0.0.24

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 (297) hide show
  1. package/README.md +82 -61
  2. package/dist/book-logo-filled.svg +55 -0
  3. package/dist/components/ui/accessibility-improvements.js +26 -23
  4. package/dist/components/ui/accordion.js +34 -24
  5. package/dist/components/ui/alert-dialog.js +91 -57
  6. package/dist/components/ui/alert.js +38 -26
  7. package/dist/components/ui/aspect-ratio.js +11 -3
  8. package/dist/components/ui/avatar.js +30 -8
  9. package/dist/components/ui/badge.js +20 -14
  10. package/dist/components/ui/breadcrumb.js +69 -56
  11. package/dist/components/ui/button-group.js +17 -13
  12. package/dist/components/ui/button.js +19 -15
  13. package/dist/components/ui/calendar.js +63 -59
  14. package/dist/components/ui/card.js +11 -9
  15. package/dist/components/ui/carousel.js +70 -75
  16. package/dist/components/ui/chart.js +75 -72
  17. package/dist/components/ui/checkbox.js +4 -5
  18. package/dist/components/ui/collapsible.js +12 -14
  19. package/dist/components/ui/color-picker.js +60 -80
  20. package/dist/components/ui/combobox.js +153 -33
  21. package/dist/components/ui/command.js +50 -48
  22. package/dist/components/ui/context-menu.js +129 -68
  23. package/dist/components/ui/dialog.js +65 -60
  24. package/dist/components/ui/drawer.js +81 -40
  25. package/dist/components/ui/dropdown-menu.js +100 -87
  26. package/dist/components/ui/empty.js +7 -6
  27. package/dist/components/ui/field.js +44 -47
  28. package/dist/components/ui/form.js +22 -23
  29. package/dist/components/ui/hover-card.js +14 -11
  30. package/dist/components/ui/input-group.js +16 -13
  31. package/dist/components/ui/input-otp.js +37 -32
  32. package/dist/components/ui/input.js +9 -7
  33. package/dist/components/ui/item.js +42 -36
  34. package/dist/components/ui/kbd.js +2 -1
  35. package/dist/components/ui/label.js +6 -7
  36. package/dist/components/ui/menubar.js +124 -93
  37. package/dist/components/ui/navigation-menu.js +79 -54
  38. package/dist/components/ui/pagination.js +78 -58
  39. package/dist/components/ui/popover.js +35 -16
  40. package/dist/components/ui/progress.js +37 -13
  41. package/dist/components/ui/radio-group.js +14 -14
  42. package/dist/components/ui/resizable.js +24 -15
  43. package/dist/components/ui/scroll-area.js +29 -11
  44. package/dist/components/ui/select.js +98 -56
  45. package/dist/components/ui/separator.js +5 -7
  46. package/dist/components/ui/sheet.js +47 -42
  47. package/dist/components/ui/sidebar.js +279 -248
  48. package/dist/components/ui/skeleton.js +4 -40
  49. package/dist/components/ui/slider.js +27 -29
  50. package/dist/components/ui/sonner.js +27 -10
  51. package/dist/components/ui/spinner.js +2 -0
  52. package/dist/components/ui/switch.js +3 -4
  53. package/dist/components/ui/table.js +6 -6
  54. package/dist/components/ui/tabs.js +45 -22
  55. package/dist/components/ui/textarea.js +3 -2
  56. package/dist/components/ui/theme-toggle.js +38 -41
  57. package/dist/components/ui/toggle-group.js +30 -21
  58. package/dist/components/ui/toggle.js +8 -9
  59. package/dist/components/ui/tooltip.js +28 -23
  60. package/dist/index.css +1 -1
  61. package/dist/index.d.ts +446 -468
  62. package/dist/index.js +58 -58
  63. package/package.json +42 -41
  64. package/src/App.tsx +28 -34
  65. package/src/components/branding/assets/book-logo-filled.svg +55 -0
  66. package/src/components/branding/logo/logo.tsx +56 -0
  67. package/src/components/ui/accessibility-improvements.stories.tsx +18 -0
  68. package/src/components/ui/accessibility-improvements.test.tsx +13 -0
  69. package/src/components/ui/accessibility-improvements.tsx +16 -6
  70. package/src/components/ui/accordion.stories.tsx +19 -0
  71. package/src/components/ui/accordion.test.tsx +14 -0
  72. package/src/components/ui/accordion.tsx +28 -20
  73. package/src/components/ui/alert-dialog.stories.tsx +19 -0
  74. package/src/components/ui/alert-dialog.test.tsx +14 -0
  75. package/src/components/ui/alert-dialog.tsx +159 -123
  76. package/src/components/ui/alert.stories.tsx +19 -0
  77. package/src/components/ui/alert.test.tsx +14 -0
  78. package/src/components/ui/alert.tsx +60 -41
  79. package/src/components/ui/aspect-ratio.stories.tsx +19 -0
  80. package/src/components/ui/aspect-ratio.test.tsx +14 -0
  81. package/src/components/ui/aspect-ratio.tsx +19 -2
  82. package/src/components/ui/attachment.stories.tsx +19 -0
  83. package/src/components/ui/attachment.test.tsx +14 -0
  84. package/src/components/ui/attachment.tsx +209 -0
  85. package/src/components/ui/avatar.stories.tsx +19 -0
  86. package/src/components/ui/avatar.test.tsx +14 -0
  87. package/src/components/ui/avatar.tsx +73 -17
  88. package/src/components/ui/badge.stories.tsx +19 -0
  89. package/src/components/ui/badge.test.tsx +14 -0
  90. package/src/components/ui/badge.tsx +33 -25
  91. package/src/components/ui/breadcrumb.stories.tsx +19 -0
  92. package/src/components/ui/breadcrumb.test.tsx +14 -0
  93. package/src/components/ui/breadcrumb.tsx +98 -86
  94. package/src/components/ui/bubble.stories.tsx +19 -0
  95. package/src/components/ui/bubble.test.tsx +14 -0
  96. package/src/components/ui/bubble.tsx +130 -0
  97. package/src/components/ui/button-group.stories.tsx +19 -0
  98. package/src/components/ui/button-group.test.tsx +14 -0
  99. package/src/components/ui/button-group.tsx +26 -20
  100. package/src/components/ui/button.stories.tsx +19 -0
  101. package/src/components/ui/button.test.tsx +14 -0
  102. package/src/components/ui/button.tsx +30 -31
  103. package/src/components/ui/calendar.stories.tsx +19 -0
  104. package/src/components/ui/calendar.test.tsx +14 -0
  105. package/src/components/ui/calendar.tsx +28 -26
  106. package/src/components/ui/card.stories.tsx +19 -0
  107. package/src/components/ui/card.test.tsx +14 -0
  108. package/src/components/ui/card.tsx +30 -17
  109. package/src/components/ui/carousel.stories.tsx +19 -0
  110. package/src/components/ui/carousel.test.tsx +14 -0
  111. package/src/components/ui/carousel.tsx +115 -135
  112. package/src/components/ui/chart.stories.tsx +18 -0
  113. package/src/components/ui/chart.test.tsx +13 -0
  114. package/src/components/ui/chart.tsx +220 -216
  115. package/src/components/ui/checkbox.stories.tsx +19 -0
  116. package/src/components/ui/checkbox.test.tsx +14 -0
  117. package/src/components/ui/checkbox.tsx +7 -10
  118. package/src/components/ui/collapsible.stories.tsx +19 -0
  119. package/src/components/ui/collapsible.test.tsx +14 -0
  120. package/src/components/ui/collapsible.tsx +11 -26
  121. package/src/components/ui/color-picker.stories.tsx +19 -0
  122. package/src/components/ui/color-picker.test.tsx +14 -0
  123. package/src/components/ui/color-picker.tsx +36 -26
  124. package/src/components/ui/combobox.stories.tsx +19 -0
  125. package/src/components/ui/combobox.test.tsx +14 -0
  126. package/src/components/ui/combobox.tsx +281 -96
  127. package/src/components/ui/command.stories.tsx +19 -0
  128. package/src/components/ui/command.test.tsx +14 -0
  129. package/src/components/ui/command.tsx +59 -47
  130. package/src/components/ui/context-menu.stories.tsx +19 -0
  131. package/src/components/ui/context-menu.test.tsx +14 -0
  132. package/src/components/ui/context-menu.tsx +231 -160
  133. package/src/components/ui/dialog.stories.tsx +19 -0
  134. package/src/components/ui/dialog.test.tsx +14 -0
  135. package/src/components/ui/dialog.tsx +81 -72
  136. package/src/components/ui/direction.stories.tsx +19 -0
  137. package/src/components/ui/direction.test.tsx +14 -0
  138. package/src/components/ui/direction.tsx +6 -0
  139. package/src/components/ui/drawer.stories.tsx +19 -0
  140. package/src/components/ui/drawer.test.tsx +14 -0
  141. package/src/components/ui/drawer.tsx +143 -64
  142. package/src/components/ui/dropdown-menu.stories.tsx +19 -0
  143. package/src/components/ui/dropdown-menu.test.tsx +14 -0
  144. package/src/components/ui/dropdown-menu.tsx +199 -153
  145. package/src/components/ui/empty.stories.tsx +18 -0
  146. package/src/components/ui/empty.test.tsx +13 -0
  147. package/src/components/ui/empty.tsx +11 -9
  148. package/src/components/ui/field.stories.tsx +18 -0
  149. package/src/components/ui/field.test.tsx +13 -0
  150. package/src/components/ui/field.tsx +26 -32
  151. package/src/components/ui/form.stories.tsx +22 -0
  152. package/src/components/ui/form.test.tsx +15 -0
  153. package/src/components/ui/form.tsx +1 -2
  154. package/src/components/ui/hover-card.stories.tsx +19 -0
  155. package/src/components/ui/hover-card.test.tsx +14 -0
  156. package/src/components/ui/hover-card.tsx +34 -32
  157. package/src/components/ui/input-group.stories.tsx +18 -0
  158. package/src/components/ui/input-group.test.tsx +13 -0
  159. package/src/components/ui/input-group.tsx +21 -28
  160. package/src/components/ui/input-otp.stories.tsx +19 -0
  161. package/src/components/ui/input-otp.test.tsx +14 -0
  162. package/src/components/ui/input-otp.tsx +58 -42
  163. package/src/components/ui/input.stories.tsx +19 -0
  164. package/src/components/ui/input.test.tsx +14 -0
  165. package/src/components/ui/input.tsx +9 -8
  166. package/src/components/ui/item.stories.tsx +18 -0
  167. package/src/components/ui/item.test.tsx +13 -0
  168. package/src/components/ui/item.tsx +38 -28
  169. package/src/components/ui/kbd.stories.tsx +18 -0
  170. package/src/components/ui/kbd.test.tsx +13 -0
  171. package/src/components/ui/kbd.tsx +3 -3
  172. package/src/components/ui/label.stories.tsx +18 -0
  173. package/src/components/ui/label.test.tsx +13 -0
  174. package/src/components/ui/label.tsx +7 -11
  175. package/src/components/ui/marker.stories.tsx +19 -0
  176. package/src/components/ui/marker.test.tsx +14 -0
  177. package/src/components/ui/marker.tsx +73 -0
  178. package/src/components/ui/menubar.stories.tsx +19 -0
  179. package/src/components/ui/menubar.test.tsx +14 -0
  180. package/src/components/ui/menubar.tsx +226 -202
  181. package/src/components/ui/message-scroller.stories.tsx +19 -0
  182. package/src/components/ui/message-scroller.test.tsx +14 -0
  183. package/src/components/ui/message-scroller.tsx +131 -0
  184. package/src/components/ui/message.stories.tsx +19 -0
  185. package/src/components/ui/message.test.tsx +14 -0
  186. package/src/components/ui/message.tsx +94 -0
  187. package/src/components/ui/native-select.stories.tsx +18 -0
  188. package/src/components/ui/native-select.test.tsx +13 -0
  189. package/src/components/ui/native-select.tsx +63 -0
  190. package/src/components/ui/navigation-menu.stories.tsx +19 -0
  191. package/src/components/ui/navigation-menu.test.tsx +14 -0
  192. package/src/components/ui/navigation-menu.tsx +145 -103
  193. package/src/components/ui/pagination.stories.tsx +19 -0
  194. package/src/components/ui/pagination.test.tsx +14 -0
  195. package/src/components/ui/pagination.tsx +114 -83
  196. package/src/components/ui/popover.stories.tsx +19 -0
  197. package/src/components/ui/popover.test.tsx +14 -0
  198. package/src/components/ui/popover.tsx +68 -33
  199. package/src/components/ui/progress.stories.tsx +19 -0
  200. package/src/components/ui/progress.test.tsx +14 -0
  201. package/src/components/ui/progress.tsx +67 -15
  202. package/src/components/ui/questionnaire.stories.tsx +19 -0
  203. package/src/components/ui/questionnaire.test.tsx +14 -0
  204. package/src/components/ui/questionnaire.tsx +324 -0
  205. package/src/components/ui/radio-group.stories.tsx +19 -0
  206. package/src/components/ui/radio-group.test.tsx +14 -0
  207. package/src/components/ui/radio-group.tsx +15 -20
  208. package/src/components/ui/resizable.stories.tsx +19 -0
  209. package/src/components/ui/resizable.test.tsx +14 -0
  210. package/src/components/ui/resizable.tsx +37 -30
  211. package/src/components/ui/scroll-area.stories.tsx +19 -0
  212. package/src/components/ui/scroll-area.test.tsx +14 -0
  213. package/src/components/ui/scroll-area.tsx +41 -19
  214. package/src/components/ui/select.stories.tsx +19 -0
  215. package/src/components/ui/select.test.tsx +14 -0
  216. package/src/components/ui/select.tsx +145 -78
  217. package/src/components/ui/separator.stories.tsx +19 -0
  218. package/src/components/ui/separator.test.tsx +14 -0
  219. package/src/components/ui/separator.tsx +8 -11
  220. package/src/components/ui/sheet.stories.tsx +19 -0
  221. package/src/components/ui/sheet.test.tsx +14 -0
  222. package/src/components/ui/sheet.tsx +56 -71
  223. package/src/components/ui/sidebar.stories.tsx +18 -0
  224. package/src/components/ui/sidebar.test.tsx +13 -0
  225. package/src/components/ui/sidebar.tsx +469 -513
  226. package/src/components/ui/skeleton.stories.tsx +19 -0
  227. package/src/components/ui/skeleton.test.tsx +14 -0
  228. package/src/components/ui/skeleton.tsx +8 -114
  229. package/src/components/ui/slider.stories.tsx +19 -0
  230. package/src/components/ui/slider.test.tsx +14 -0
  231. package/src/components/ui/slider.tsx +31 -40
  232. package/src/components/ui/sonner.stories.tsx +19 -0
  233. package/src/components/ui/sonner.test.tsx +14 -0
  234. package/src/components/ui/sonner.tsx +39 -7
  235. package/src/components/ui/spinner.stories.tsx +18 -0
  236. package/src/components/ui/spinner.test.tsx +13 -0
  237. package/src/components/ui/spinner.tsx +3 -7
  238. package/src/components/ui/switch.stories.tsx +19 -0
  239. package/src/components/ui/switch.test.tsx +14 -0
  240. package/src/components/ui/switch.tsx +4 -7
  241. package/src/components/ui/table.stories.tsx +19 -0
  242. package/src/components/ui/table.test.tsx +14 -0
  243. package/src/components/ui/table.tsx +17 -17
  244. package/src/components/ui/tabs.stories.tsx +19 -0
  245. package/src/components/ui/tabs.test.tsx +14 -0
  246. package/src/components/ui/tabs.tsx +71 -31
  247. package/src/components/ui/textarea.stories.tsx +19 -0
  248. package/src/components/ui/textarea.test.tsx +14 -0
  249. package/src/components/ui/textarea.tsx +7 -5
  250. package/src/components/ui/theme-toggle.stories.tsx +18 -0
  251. package/src/components/ui/theme-toggle.test.tsx +13 -0
  252. package/src/components/ui/theme-toggle.tsx +35 -37
  253. package/src/components/ui/toggle-group.stories.tsx +19 -0
  254. package/src/components/ui/toggle-group.test.tsx +14 -0
  255. package/src/components/ui/toggle-group.tsx +41 -25
  256. package/src/components/ui/toggle.stories.tsx +19 -0
  257. package/src/components/ui/toggle.test.tsx +14 -0
  258. package/src/components/ui/toggle.tsx +17 -19
  259. package/src/components/ui/tooltip.stories.tsx +19 -0
  260. package/src/components/ui/tooltip.test.tsx +14 -0
  261. package/src/components/ui/tooltip.tsx +53 -44
  262. package/src/demo/AIDemo.tsx +50 -0
  263. package/src/demo/ButtonsDemo.tsx +41 -18
  264. package/src/demo/CalendarDemo.tsx +1 -1
  265. package/src/demo/FeedbackDemo.tsx +4 -6
  266. package/src/demo/FormsDemo.tsx +34 -11
  267. package/src/demo/NavigationDemo.tsx +38 -13
  268. package/src/demo/OverlaysDemo.tsx +22 -36
  269. package/src/demo/update-demos.js +82 -0
  270. package/src/index.css +66 -46
  271. package/src/lib/utils.ts +3 -3
  272. package/src/stories/Button.stories.ts +54 -0
  273. package/src/stories/Button.tsx +36 -0
  274. package/src/stories/Configure.mdx +388 -0
  275. package/src/stories/Header.stories.ts +34 -0
  276. package/src/stories/Header.tsx +55 -0
  277. package/src/stories/Page.stories.ts +33 -0
  278. package/src/stories/Page.tsx +73 -0
  279. package/src/stories/assets/accessibility.png +0 -0
  280. package/src/stories/assets/accessibility.svg +1 -0
  281. package/src/stories/assets/addon-library.png +0 -0
  282. package/src/stories/assets/assets.png +0 -0
  283. package/src/stories/assets/avif-test-image.avif +0 -0
  284. package/src/stories/assets/context.png +0 -0
  285. package/src/stories/assets/discord.svg +1 -0
  286. package/src/stories/assets/docs.png +0 -0
  287. package/src/stories/assets/figma-plugin.png +0 -0
  288. package/src/stories/assets/github.svg +1 -0
  289. package/src/stories/assets/share.png +0 -0
  290. package/src/stories/assets/styling.png +0 -0
  291. package/src/stories/assets/testing.png +0 -0
  292. package/src/stories/assets/theming.png +0 -0
  293. package/src/stories/assets/tutorials.svg +1 -0
  294. package/src/stories/assets/youtube.svg +1 -0
  295. package/src/stories/button.css +30 -0
  296. package/src/stories/header.css +32 -0
  297. package/src/stories/page.css +68 -0
@@ -12,8 +12,7 @@ function FieldSet({ className, ...props }: React.ComponentProps<"fieldset">) {
12
12
  <fieldset
13
13
  data-slot="field-set"
14
14
  className={cn(
15
- "flex flex-col gap-6",
16
- "has-[>[data-slot=checkbox-group]]:gap-3 has-[>[data-slot=radio-group]]:gap-3",
15
+ "flex flex-col gap-4 has-[>[data-slot=checkbox-group]]:gap-3 has-[>[data-slot=radio-group]]:gap-3",
17
16
  className
18
17
  )}
19
18
  {...props}
@@ -31,9 +30,7 @@ function FieldLegend({
31
30
  data-slot="field-legend"
32
31
  data-variant={variant}
33
32
  className={cn(
34
- "mb-3 font-medium",
35
- "data-[variant=legend]:text-base",
36
- "data-[variant=label]:text-sm",
33
+ "mb-1.5 font-medium data-[variant=label]:text-sm data-[variant=legend]:text-base",
37
34
  className
38
35
  )}
39
36
  {...props}
@@ -46,7 +43,7 @@ function FieldGroup({ className, ...props }: React.ComponentProps<"div">) {
46
43
  <div
47
44
  data-slot="field-group"
48
45
  className={cn(
49
- "group/field-group @container/field-group flex w-full flex-col gap-7 data-[slot=checkbox-group]:gap-3 [&>[data-slot=field-group]]:gap-4",
46
+ "group/field-group @container/field-group flex w-full flex-col gap-5 data-[slot=checkbox-group]:gap-3 *:data-[slot=field-group]:gap-4",
50
47
  className
51
48
  )}
52
49
  {...props}
@@ -55,21 +52,15 @@ function FieldGroup({ className, ...props }: React.ComponentProps<"div">) {
55
52
  }
56
53
 
57
54
  const fieldVariants = cva(
58
- "group/field data-[invalid=true]:text-destructive flex w-full gap-3",
55
+ "group/field flex w-full gap-2 data-[invalid=true]:text-destructive",
59
56
  {
60
57
  variants: {
61
58
  orientation: {
62
- vertical: ["flex-col [&>*]:w-full [&>.sr-only]:w-auto"],
63
- horizontal: [
64
- "flex-row items-center",
65
- "[&>[data-slot=field-label]]:flex-auto",
66
- "has-[>[data-slot=field-content]]:[&>[role=checkbox],[role=radio]]:mt-px has-[>[data-slot=field-content]]:items-start",
67
- ],
68
- responsive: [
69
- "@md/field-group:flex-row @md/field-group:items-center @md/field-group:[&>*]:w-auto flex-col [&>*]:w-full [&>.sr-only]:w-auto",
70
- "@md/field-group:[&>[data-slot=field-label]]:flex-auto",
71
- "@md/field-group:has-[>[data-slot=field-content]]:items-start @md/field-group:has-[>[data-slot=field-content]]:[&>[role=checkbox],[role=radio]]:mt-px",
72
- ],
59
+ vertical: "flex-col *:w-full [&>.sr-only]:w-auto",
60
+ horizontal:
61
+ "flex-row items-center has-[>[data-slot=field-content]]:items-start *:data-[slot=field-label]:flex-auto has-[>[data-slot=field-content]]:[&>[role=checkbox],[role=radio]]:mt-px",
62
+ responsive:
63
+ "flex-col *:w-full @md/field-group:flex-row @md/field-group:items-center @md/field-group:*:w-auto @md/field-group:has-[>[data-slot=field-content]]:items-start @md/field-group:*:data-[slot=field-label]:flex-auto [&>.sr-only]:w-auto @md/field-group:has-[>[data-slot=field-content]]:[&>[role=checkbox],[role=radio]]:mt-px",
73
64
  },
74
65
  },
75
66
  defaultVariants: {
@@ -99,7 +90,7 @@ function FieldContent({ className, ...props }: React.ComponentProps<"div">) {
99
90
  <div
100
91
  data-slot="field-content"
101
92
  className={cn(
102
- "group/field-content flex flex-1 flex-col gap-1.5 leading-snug",
93
+ "group/field-content flex flex-1 flex-col gap-0.5 leading-snug",
103
94
  className
104
95
  )}
105
96
  {...props}
@@ -115,9 +106,8 @@ function FieldLabel({
115
106
  <Label
116
107
  data-slot="field-label"
117
108
  className={cn(
118
- "group/field-label peer/field-label flex w-fit gap-2 leading-snug group-data-[disabled=true]/field:opacity-50",
119
- "has-[>[data-slot=field]]:w-full has-[>[data-slot=field]]:flex-col has-[>[data-slot=field]]:rounded-md has-[>[data-slot=field]]:border [&>[data-slot=field]]:p-4",
120
- "has-data-[state=checked]:bg-primary/5 has-data-[state=checked]:border-primary dark:has-data-[state=checked]:bg-primary/10",
109
+ "group/field-label peer/field-label flex w-fit gap-2 leading-snug group-data-[disabled=true]/field:opacity-50 has-data-checked:border-primary/30 has-data-checked:bg-primary/5 has-[>[data-slot=field]]:rounded-lg has-[>[data-slot=field]]:border has-[>[data-slot=field]]:not-has-[:disabled,[data-disabled]]:hover:bg-muted/50 has-[>[data-slot=field]]:has-[:focus-visible]:border-ring has-[>[data-slot=field]]:has-[:focus-visible]:ring-3 has-[>[data-slot=field]]:has-[:focus-visible]:ring-ring/50 *:data-[slot=field]:p-2.5 dark:has-data-checked:border-primary/20 dark:has-data-checked:bg-primary/10",
110
+ "has-[>[data-slot=field]]:w-full has-[>[data-slot=field]]:flex-col",
121
111
  className
122
112
  )}
123
113
  {...props}
@@ -130,7 +120,7 @@ function FieldTitle({ className, ...props }: React.ComponentProps<"div">) {
130
120
  <div
131
121
  data-slot="field-label"
132
122
  className={cn(
133
- "flex w-fit items-center gap-2 text-sm font-medium leading-snug group-data-[disabled=true]/field:opacity-50",
123
+ "flex w-fit items-center gap-2 text-sm font-medium group-data-[disabled=true]/field:opacity-50",
134
124
  className
135
125
  )}
136
126
  {...props}
@@ -143,9 +133,9 @@ function FieldDescription({ className, ...props }: React.ComponentProps<"p">) {
143
133
  <p
144
134
  data-slot="field-description"
145
135
  className={cn(
146
- "text-muted-foreground text-sm font-normal leading-normal group-has-[[data-orientation=horizontal]]/field:text-balance",
147
- "nth-last-2:-mt-1 last:mt-0 [[data-variant=legend]+&]:-mt-1.5",
148
- "[&>a:hover]:text-primary [&>a]:underline [&>a]:underline-offset-4",
136
+ "text-left text-sm leading-normal font-normal text-muted-foreground group-has-data-horizontal/field:text-balance [[data-variant=legend]+&]:-mt-1.5",
137
+ "last:mt-0 nth-last-2:-mt-1",
138
+ "[&>a]:underline [&>a]:underline-offset-4 [&>a:hover]:text-primary",
149
139
  className
150
140
  )}
151
141
  {...props}
@@ -173,7 +163,7 @@ function FieldSeparator({
173
163
  <Separator className="absolute inset-0 top-1/2" />
174
164
  {children && (
175
165
  <span
176
- className="bg-background text-muted-foreground relative mx-auto block w-fit px-2"
166
+ className="relative mx-auto block w-fit bg-background px-2 text-muted-foreground"
177
167
  data-slot="field-separator-content"
178
168
  >
179
169
  {children}
@@ -196,17 +186,21 @@ function FieldError({
196
186
  return children
197
187
  }
198
188
 
199
- if (!errors) {
189
+ if (!errors?.length) {
200
190
  return null
201
191
  }
202
192
 
203
- if (errors?.length === 1 && errors[0]?.message) {
204
- return errors[0].message
193
+ const uniqueErrors = [
194
+ ...new Map(errors.map((error) => [error?.message, error])).values(),
195
+ ]
196
+
197
+ if (uniqueErrors?.length == 1) {
198
+ return uniqueErrors[0]?.message
205
199
  }
206
200
 
207
201
  return (
208
202
  <ul className="ml-4 flex list-disc flex-col gap-1">
209
- {errors.map(
203
+ {uniqueErrors.map(
210
204
  (error, index) =>
211
205
  error?.message && <li key={index}>{error.message}</li>
212
206
  )}
@@ -222,7 +216,7 @@ function FieldError({
222
216
  <div
223
217
  role="alert"
224
218
  data-slot="field-error"
225
- className={cn("text-destructive text-sm font-normal", className)}
219
+ className={cn("text-sm font-normal text-destructive", className)}
226
220
  {...props}
227
221
  >
228
222
  {content}
@@ -0,0 +1,22 @@
1
+ import type { Meta, StoryObj } from '@storybook/react-vite';
2
+ import { Form, FormField, FormItem, FormLabel, FormControl, FormDescription, FormMessage } from "./form"; import { useForm } from "react-hook-form"; import { Input } from "./input";
3
+
4
+ const meta = {
5
+ title: 'Design System/Form',
6
+ parameters: { layout: 'centered' },
7
+ tags: ['autodocs'],
8
+ } satisfies Meta;
9
+
10
+ export default meta;
11
+ type Story = StoryObj<typeof meta>;
12
+
13
+ export const Default: Story = {
14
+ render: () => {
15
+ const form = useForm({ defaultValues: { username: "" }});
16
+ return (
17
+ <div className="p-4 w-full h-full flex items-center justify-center">
18
+ <Form {...form}><form className="space-y-8"><FormField control={form.control} name="username" render={({ field }) => (<FormItem><FormLabel>Username</FormLabel><FormControl><Input placeholder="shadcn" {...field} /></FormControl><FormDescription>This is your public display name.</FormDescription><FormMessage /></FormItem>)} /></form></Form>
19
+ </div>
20
+ );
21
+ }
22
+ };
@@ -0,0 +1,15 @@
1
+ import { render } from '@testing-library/react';
2
+ import { axe } from 'jest-axe';
3
+ import { Form, FormField, FormItem, FormLabel, FormControl, FormDescription, FormMessage } from "./form"; import { useForm } from "react-hook-form"; import { Input } from "./input";
4
+
5
+ describe('Form Accessibility', () => {
6
+ it('should have no accessibility violations in basic render', async () => {
7
+ const FormWrapper = () => {
8
+ const form = useForm({ defaultValues: { username: "" }});
9
+ return (<Form {...form}><form className="space-y-8"><FormField control={form.control} name="username" render={({ field }) => (<FormItem><FormLabel>Username</FormLabel><FormControl><Input placeholder="shadcn" {...field} /></FormControl><FormDescription>This is your public display name.</FormDescription><FormMessage /></FormItem>)} /></form></Form>);
10
+ };
11
+ const { container } = render(<FormWrapper />);
12
+ const results = await axe(container);
13
+ expect(results).toHaveNoViolations();
14
+ });
15
+ });
@@ -1,7 +1,6 @@
1
1
  "use client";
2
2
 
3
3
  import * as React from "react";
4
- import * as LabelPrimitive from "@radix-ui/react-label";
5
4
  import { Slot } from "@radix-ui/react-slot";
6
5
  import {
7
6
  Controller,
@@ -90,7 +89,7 @@ function FormItem({ className, ...props }: React.ComponentProps<"div">) {
90
89
  function FormLabel({
91
90
  className,
92
91
  ...props
93
- }: React.ComponentProps<typeof LabelPrimitive.Root>) {
92
+ }: React.ComponentProps<"label">) {
94
93
  const { error, formItemId } = useFormField();
95
94
 
96
95
  return (
@@ -0,0 +1,19 @@
1
+ import type { Meta, StoryObj } from '@storybook/react-vite';
2
+ import { HoverCard, HoverCardTrigger, HoverCardContent } from "./hover-card";
3
+
4
+ const meta = {
5
+ title: 'Design System/HoverCard',
6
+ parameters: { layout: 'centered' },
7
+ tags: ['autodocs'],
8
+ } satisfies Meta;
9
+
10
+ export default meta;
11
+ type Story = StoryObj<typeof meta>;
12
+
13
+ export const Default: Story = {
14
+ render: () => (
15
+ <div className="p-4 w-full h-full flex items-center justify-center">
16
+ <HoverCard><HoverCardTrigger className="underline cursor-pointer">@nextjs</HoverCardTrigger><HoverCardContent>The React Framework – created and maintained by @vercel.</HoverCardContent></HoverCard>
17
+ </div>
18
+ )
19
+ };
@@ -0,0 +1,14 @@
1
+ import { render } from '@testing-library/react';
2
+ import { axe } from 'jest-axe';
3
+ import { HoverCard, HoverCardTrigger, HoverCardContent } from "./hover-card";
4
+
5
+ describe('HoverCard Accessibility', () => {
6
+ it('should have no accessibility violations in basic render', async () => {
7
+ const Wrapper = () => (
8
+ <HoverCard><HoverCardTrigger className="underline cursor-pointer">@nextjs</HoverCardTrigger><HoverCardContent>The React Framework – created and maintained by @vercel.</HoverCardContent></HoverCard>
9
+ );
10
+ const { container } = render(<Wrapper />);
11
+ const results = await axe(container);
12
+ expect(results).toHaveNoViolations();
13
+ });
14
+ });
@@ -1,49 +1,51 @@
1
- "use client";
1
+ "use client"
2
2
 
3
- import * as React from "react";
4
- import * as HoverCardPrimitive from "@radix-ui/react-hover-card";
3
+ import { PreviewCard as PreviewCardPrimitive } from "@base-ui/react/preview-card"
5
4
 
6
- import { cn } from "@/lib/utils";
5
+ import { cn } from "@/lib/utils"
7
6
 
8
- function HoverCard({
9
- ...props
10
- }: React.ComponentProps<typeof HoverCardPrimitive.Root>) {
11
- return <HoverCardPrimitive.Root data-slot="hover-card" {...props} />;
7
+ function HoverCard({ ...props }: PreviewCardPrimitive.Root.Props) {
8
+ return <PreviewCardPrimitive.Root data-slot="hover-card" {...props} />
12
9
  }
13
10
 
14
- function HoverCardTrigger({
15
- className,
16
- ...props
17
- }: React.ComponentProps<typeof HoverCardPrimitive.Trigger>) {
11
+ function HoverCardTrigger({ ...props }: PreviewCardPrimitive.Trigger.Props) {
18
12
  return (
19
- <HoverCardPrimitive.Trigger
20
- data-slot="hover-card-trigger"
21
- className={cn("cursor-pointer", className)}
22
- {...props}
23
- />
24
- );
13
+ <PreviewCardPrimitive.Trigger data-slot="hover-card-trigger" {...props} />
14
+ )
25
15
  }
26
16
 
27
17
  function HoverCardContent({
28
18
  className,
29
- align = "center",
19
+ side = "bottom",
30
20
  sideOffset = 4,
21
+ align = "center",
22
+ alignOffset = 4,
31
23
  ...props
32
- }: React.ComponentProps<typeof HoverCardPrimitive.Content>) {
24
+ }: PreviewCardPrimitive.Popup.Props &
25
+ Pick<
26
+ PreviewCardPrimitive.Positioner.Props,
27
+ "align" | "alignOffset" | "side" | "sideOffset"
28
+ >) {
33
29
  return (
34
- <HoverCardPrimitive.Portal data-slot="hover-card-portal">
35
- <HoverCardPrimitive.Content
36
- data-slot="hover-card-content"
30
+ <PreviewCardPrimitive.Portal data-slot="hover-card-portal">
31
+ <PreviewCardPrimitive.Positioner
37
32
  align={align}
33
+ alignOffset={alignOffset}
34
+ side={side}
38
35
  sideOffset={sideOffset}
39
- className={cn(
40
- "bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 w-64 origin-(--radix-hover-card-content-transform-origin) rounded-md border p-4 shadow-md outline-hidden",
41
- className
42
- )}
43
- {...props}
44
- />
45
- </HoverCardPrimitive.Portal>
46
- );
36
+ className="isolate z-50"
37
+ >
38
+ <PreviewCardPrimitive.Popup
39
+ data-slot="hover-card-content"
40
+ className={cn(
41
+ "z-50 w-64 origin-(--transform-origin) rounded-lg bg-popover p-2.5 text-sm text-popover-foreground shadow-md ring-1 ring-foreground/10 outline-hidden duration-100 data-[side=bottom]:slide-in-from-top-2 data-[side=inline-end]:slide-in-from-left-2 data-[side=inline-start]:slide-in-from-right-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95",
42
+ className
43
+ )}
44
+ {...props}
45
+ />
46
+ </PreviewCardPrimitive.Positioner>
47
+ </PreviewCardPrimitive.Portal>
48
+ )
47
49
  }
48
50
 
49
- export { HoverCard, HoverCardTrigger, HoverCardContent };
51
+ export { HoverCard, HoverCardTrigger, HoverCardContent }
@@ -0,0 +1,18 @@
1
+ import type { Meta, StoryObj } from '@storybook/react-vite';
2
+
3
+ const meta = {
4
+ title: 'Design System/InputGroup',
5
+ parameters: { layout: 'centered' },
6
+ tags: ['autodocs'],
7
+ } satisfies Meta;
8
+
9
+ export default meta;
10
+ type Story = StoryObj<typeof meta>;
11
+
12
+ export const Default: Story = {
13
+ render: () => (
14
+ <div className="p-4 w-full h-full flex items-center justify-center">
15
+ <div className="p-4 border rounded bg-muted/50 text-muted-foreground text-sm">InputGroup — needs manual template</div>
16
+ </div>
17
+ )
18
+ };
@@ -0,0 +1,13 @@
1
+ import { render } from '@testing-library/react';
2
+ import { axe } from 'jest-axe';
3
+
4
+ describe('InputGroup Accessibility', () => {
5
+ it('should have no accessibility violations in basic render', async () => {
6
+ const Wrapper = () => (
7
+ <div className="p-4 border rounded bg-muted/50 text-muted-foreground text-sm">InputGroup — needs manual template</div>
8
+ );
9
+ const { container } = render(<Wrapper />);
10
+ const results = await axe(container);
11
+ expect(results).toHaveNoViolations();
12
+ });
13
+ });
@@ -14,21 +14,7 @@ function InputGroup({ className, ...props }: React.ComponentProps<"div">) {
14
14
  data-slot="input-group"
15
15
  role="group"
16
16
  className={cn(
17
- "group/input-group border-input dark:bg-input/30 shadow-xs relative flex w-full items-center rounded-md border outline-none transition-[color,box-shadow]",
18
- "h-9 has-[>textarea]:h-auto",
19
-
20
- // Variants based on alignment.
21
- "has-[>[data-align=inline-start]]:[&>input]:pl-2",
22
- "has-[>[data-align=inline-end]]:[&>input]:pr-2",
23
- "has-[>[data-align=block-start]]:h-auto has-[>[data-align=block-start]]:flex-col has-[>[data-align=block-start]]:[&>input]:pb-3",
24
- "has-[>[data-align=block-end]]:h-auto has-[>[data-align=block-end]]:flex-col has-[>[data-align=block-end]]:[&>input]:pt-3",
25
-
26
- // Focus state.
27
- "has-[[data-slot=input-group-control]:focus-visible]:ring-ring has-[[data-slot=input-group-control]:focus-visible]:ring-1",
28
-
29
- // Error state.
30
- "has-[[data-slot][aria-invalid=true]]:ring-destructive/20 has-[[data-slot][aria-invalid=true]]:border-destructive dark:has-[[data-slot][aria-invalid=true]]:ring-destructive/40",
31
-
17
+ "group/input-group relative flex h-8 w-full min-w-0 items-center rounded-lg border border-input transition-colors outline-none in-data-[slot=combobox-content]:focus-within:border-inherit in-data-[slot=combobox-content]:focus-within:ring-0 has-disabled:bg-input/50 has-disabled:opacity-50 has-[[data-slot=input-group-control]:focus-visible]:border-ring has-[[data-slot=input-group-control]:focus-visible]:ring-3 has-[[data-slot=input-group-control]:focus-visible]:ring-ring/50 has-[[data-slot][aria-invalid=true]]:border-destructive has-[[data-slot][aria-invalid=true]]:ring-3 has-[[data-slot][aria-invalid=true]]:ring-destructive/20 has-[>[data-align=block-end]]:h-auto has-[>[data-align=block-end]]:flex-col has-[>[data-align=block-start]]:h-auto has-[>[data-align=block-start]]:flex-col has-[>textarea]:h-auto dark:bg-input/30 dark:has-disabled:bg-input/80 dark:has-[[data-slot][aria-invalid=true]]:ring-destructive/40 has-[>[data-align=block-end]]:[&>input]:pt-3 has-[>[data-align=block-start]]:[&>input]:pb-3 has-[>[data-align=inline-end]]:[&>input]:pr-1.5 has-[>[data-align=inline-start]]:[&>input]:pl-1.5",
32
18
  className
33
19
  )}
34
20
  {...props}
@@ -37,18 +23,18 @@ function InputGroup({ className, ...props }: React.ComponentProps<"div">) {
37
23
  }
38
24
 
39
25
  const inputGroupAddonVariants = cva(
40
- "text-muted-foreground flex h-auto cursor-text select-none items-center justify-center gap-2 py-1.5 text-sm font-medium group-data-[disabled=true]/input-group:opacity-50 [&>kbd]:rounded-[calc(var(--radius)-5px)] [&>svg:not([class*='size-'])]:size-4",
26
+ "flex h-auto cursor-text items-center justify-center gap-2 py-1.5 text-sm font-medium text-muted-foreground select-none group-data-[disabled=true]/input-group:opacity-50 [&>kbd]:rounded-[calc(var(--radius)-5px)] [&>svg:not([class*='size-'])]:size-4",
41
27
  {
42
28
  variants: {
43
29
  align: {
44
30
  "inline-start":
45
- "order-first pl-3 has-[>button]:ml-[-0.45rem] has-[>kbd]:ml-[-0.35rem]",
31
+ "order-first pl-2 has-[>button]:ml-[-0.3rem] has-[>kbd]:ml-[-0.15rem]",
46
32
  "inline-end":
47
- "order-last pr-3 has-[>button]:mr-[-0.4rem] has-[>kbd]:mr-[-0.35rem]",
33
+ "order-last pr-2 has-[>button]:mr-[-0.3rem] has-[>kbd]:mr-[-0.15rem]",
48
34
  "block-start":
49
- "[.border-b]:pb-3 order-first w-full justify-start px-3 pt-3 group-has-[>input]/input-group:pt-2.5",
35
+ "order-first w-full justify-start px-2.5 pt-2 group-has-[>input]/input-group:pt-2 [.border-b]:pb-2",
50
36
  "block-end":
51
- "[.border-t]:pt-3 order-last w-full justify-start px-3 pb-3 group-has-[>input]/input-group:pb-2.5",
37
+ "order-last w-full justify-start px-2.5 pb-2 group-has-[>input]/input-group:pb-2 [.border-t]:pt-2",
52
38
  },
53
39
  },
54
40
  defaultVariants: {
@@ -74,6 +60,11 @@ function InputGroupAddon({
74
60
  }
75
61
  e.currentTarget.parentElement?.querySelector("input")?.focus()
76
62
  }}
63
+ onKeyDown={(e) => {
64
+ if (e.key === "Enter" || e.key === " ") {
65
+ e.currentTarget.parentElement?.querySelector("input")?.focus()
66
+ }
67
+ }}
77
68
  {...props}
78
69
  />
79
70
  )
@@ -84,10 +75,10 @@ const inputGroupButtonVariants = cva(
84
75
  {
85
76
  variants: {
86
77
  size: {
87
- xs: "h-6 gap-1 rounded-[calc(var(--radius)-5px)] px-2 has-[>svg]:px-2 [&>svg:not([class*='size-'])]:size-3.5",
88
- sm: "h-8 gap-1.5 rounded-md px-2.5 has-[>svg]:px-2.5",
78
+ xs: "h-6 gap-1 rounded-[calc(var(--radius)-3px)] px-1.5 [&>svg:not([class*='size-'])]:size-3.5",
79
+ sm: "",
89
80
  "icon-xs":
90
- "size-6 rounded-[calc(var(--radius)-5px)] p-0 has-[>svg]:p-0",
81
+ "size-6 rounded-[calc(var(--radius)-3px)] p-0 has-[>svg]:p-0",
91
82
  "icon-sm": "size-8 p-0 has-[>svg]:p-0",
92
83
  },
93
84
  },
@@ -103,8 +94,10 @@ function InputGroupButton({
103
94
  variant = "ghost",
104
95
  size = "xs",
105
96
  ...props
106
- }: Omit<React.ComponentProps<typeof Button>, "size"> &
107
- VariantProps<typeof inputGroupButtonVariants>) {
97
+ }: Omit<React.ComponentProps<typeof Button>, "size" | "type"> &
98
+ VariantProps<typeof inputGroupButtonVariants> & {
99
+ type?: "button" | "submit" | "reset"
100
+ }) {
108
101
  return (
109
102
  <Button
110
103
  type={type}
@@ -120,7 +113,7 @@ function InputGroupText({ className, ...props }: React.ComponentProps<"span">) {
120
113
  return (
121
114
  <span
122
115
  className={cn(
123
- "text-muted-foreground flex items-center gap-2 text-sm [&_svg:not([class*='size-'])]:size-4 [&_svg]:pointer-events-none",
116
+ "flex items-center gap-2 text-sm text-muted-foreground [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4",
124
117
  className
125
118
  )}
126
119
  {...props}
@@ -136,7 +129,7 @@ function InputGroupInput({
136
129
  <Input
137
130
  data-slot="input-group-control"
138
131
  className={cn(
139
- "flex-1 rounded-none border-0 bg-transparent shadow-none focus-visible:ring-0 dark:bg-transparent",
132
+ "flex-1 rounded-none border-0 bg-transparent shadow-none ring-0 focus-visible:ring-0 disabled:bg-transparent aria-invalid:ring-0 dark:bg-transparent dark:disabled:bg-transparent",
140
133
  className
141
134
  )}
142
135
  {...props}
@@ -152,7 +145,7 @@ function InputGroupTextarea({
152
145
  <Textarea
153
146
  data-slot="input-group-control"
154
147
  className={cn(
155
- "flex-1 resize-none rounded-none border-0 bg-transparent py-3 shadow-none focus-visible:ring-0 dark:bg-transparent",
148
+ "flex-1 resize-none rounded-none border-0 bg-transparent py-2 shadow-none ring-0 focus-visible:ring-0 disabled:bg-transparent aria-invalid:ring-0 dark:bg-transparent dark:disabled:bg-transparent",
156
149
  className
157
150
  )}
158
151
  {...props}
@@ -0,0 +1,19 @@
1
+ import type { Meta, StoryObj } from '@storybook/react-vite';
2
+ import { InputOTP, InputOTPGroup, InputOTPSeparator, InputOTPSlot } from "./input-otp";
3
+
4
+ const meta = {
5
+ title: 'Design System/InputOtp',
6
+ parameters: { layout: 'centered' },
7
+ tags: ['autodocs'],
8
+ } satisfies Meta;
9
+
10
+ export default meta;
11
+ type Story = StoryObj<typeof meta>;
12
+
13
+ export const Default: Story = {
14
+ render: () => (
15
+ <div className="p-4 w-full h-full flex items-center justify-center">
16
+ <div><label htmlFor="otp-input">Verification Code</label><InputOTP id="otp-input" maxLength={6}><InputOTPGroup><InputOTPSlot index={0} /><InputOTPSlot index={1} /><InputOTPSlot index={2} /></InputOTPGroup><InputOTPSeparator /><InputOTPGroup><InputOTPSlot index={3} /><InputOTPSlot index={4} /><InputOTPSlot index={5} /></InputOTPGroup></InputOTP></div>
17
+ </div>
18
+ )
19
+ };
@@ -0,0 +1,14 @@
1
+ import { render } from '@testing-library/react';
2
+ import { axe } from 'jest-axe';
3
+ import { InputOTP, InputOTPGroup, InputOTPSeparator, InputOTPSlot } from "./input-otp";
4
+
5
+ describe('InputOtp Accessibility', () => {
6
+ it('should have no accessibility violations in basic render', async () => {
7
+ const Wrapper = () => (
8
+ <div><label htmlFor="otp-input">Verification Code</label><InputOTP id="otp-input" maxLength={6}><InputOTPGroup><InputOTPSlot index={0} /><InputOTPSlot index={1} /><InputOTPSlot index={2} /></InputOTPGroup><InputOTPSeparator /><InputOTPGroup><InputOTPSlot index={3} /><InputOTPSlot index={4} /><InputOTPSlot index={5} /></InputOTPGroup></InputOTP></div>
9
+ );
10
+ const { container } = render(<Wrapper />);
11
+ const results = await axe(container);
12
+ expect(results).toHaveNoViolations();
13
+ });
14
+ });
@@ -2,47 +2,60 @@
2
2
 
3
3
  import * as React from "react"
4
4
  import { OTPInput, OTPInputContext } from "input-otp"
5
- import { Dot } from "lucide-react"
6
5
 
7
6
  import { cn } from "@/lib/utils"
7
+ import { MinusIcon } from "lucide-react"
8
8
 
9
- const InputOTP = React.forwardRef<
10
- React.ElementRef<typeof OTPInput>,
11
- React.ComponentPropsWithoutRef<typeof OTPInput>
12
- >(({ className, containerClassName, ...props }, ref) => (
13
- <OTPInput
14
- ref={ref}
15
- containerClassName={cn(
16
- "flex items-center gap-2 has-[:disabled]:opacity-50",
17
- containerClassName
18
- )}
19
- className={cn("disabled:cursor-not-allowed", className)}
20
- {...props}
21
- />
22
- ))
23
- InputOTP.displayName = "InputOTP"
9
+ function InputOTP({
10
+ className,
11
+ containerClassName,
12
+ ...props
13
+ }: React.ComponentProps<typeof OTPInput> & {
14
+ containerClassName?: string
15
+ }) {
16
+ return (
17
+ <OTPInput
18
+ data-slot="input-otp"
19
+ containerClassName={cn(
20
+ "cn-input-otp flex items-center has-disabled:opacity-50",
21
+ containerClassName
22
+ )}
23
+ spellCheck={false}
24
+ className={cn("disabled:cursor-not-allowed", className)}
25
+ {...props}
26
+ />
27
+ )
28
+ }
24
29
 
25
- const InputOTPGroup = React.forwardRef<
26
- React.ElementRef<"div">,
27
- React.ComponentPropsWithoutRef<"div">
28
- >(({ className, ...props }, ref) => (
29
- <div ref={ref} className={cn("flex items-center", className)} {...props} />
30
- ))
31
- InputOTPGroup.displayName = "InputOTPGroup"
30
+ function InputOTPGroup({ className, ...props }: React.ComponentProps<"div">) {
31
+ return (
32
+ <div
33
+ data-slot="input-otp-group"
34
+ className={cn(
35
+ "flex items-center rounded-lg has-aria-invalid:border-destructive has-aria-invalid:ring-3 has-aria-invalid:ring-destructive/20 dark:has-aria-invalid:ring-destructive/40",
36
+ className
37
+ )}
38
+ {...props}
39
+ />
40
+ )
41
+ }
32
42
 
33
- const InputOTPSlot = React.forwardRef<
34
- React.ElementRef<"div">,
35
- React.ComponentPropsWithoutRef<"div"> & { index: number }
36
- >(({ index, className, ...props }, ref) => {
43
+ function InputOTPSlot({
44
+ index,
45
+ className,
46
+ ...props
47
+ }: React.ComponentProps<"div"> & {
48
+ index: number
49
+ }) {
37
50
  const inputOTPContext = React.useContext(OTPInputContext)
38
- const { char, hasFakeCaret, isActive } = inputOTPContext.slots[index]
51
+ const { char, hasFakeCaret, isActive } = inputOTPContext?.slots[index] ?? {}
39
52
 
40
53
  return (
41
54
  <div
42
- ref={ref}
55
+ data-slot="input-otp-slot"
56
+ data-active={isActive}
43
57
  className={cn(
44
- "relative flex h-10 w-10 items-center justify-center border-y border-r border-input text-sm transition-all first:rounded-l-md first:border-l last:rounded-r-md",
45
- isActive && "z-10 ring-2 ring-ring ring-offset-background",
58
+ "relative flex size-8 items-center justify-center border-y border-r border-input text-sm transition-all outline-none first:rounded-l-lg first:border-l last:rounded-r-lg aria-invalid:border-destructive data-[active=true]:z-10 data-[active=true]:border-ring data-[active=true]:ring-3 data-[active=true]:ring-ring/50 data-[active=true]:aria-invalid:border-destructive data-[active=true]:aria-invalid:ring-destructive/20 dark:bg-input/30 dark:data-[active=true]:aria-invalid:ring-destructive/40",
46
59
  className
47
60
  )}
48
61
  {...props}
@@ -55,17 +68,20 @@ const InputOTPSlot = React.forwardRef<
55
68
  )}
56
69
  </div>
57
70
  )
58
- })
59
- InputOTPSlot.displayName = "InputOTPSlot"
71
+ }
60
72
 
61
- const InputOTPSeparator = React.forwardRef<
62
- React.ElementRef<"div">,
63
- React.ComponentPropsWithoutRef<"div">
64
- >(({ ...props }, ref) => (
65
- <div ref={ref} role="separator" {...props}>
66
- <Dot />
67
- </div>
68
- ))
69
- InputOTPSeparator.displayName = "InputOTPSeparator"
73
+ function InputOTPSeparator({ ...props }: React.ComponentProps<"div">) {
74
+ return (
75
+ <div
76
+ data-slot="input-otp-separator"
77
+ className="flex items-center [&_svg:not([class*='size-'])]:size-4"
78
+ role="separator"
79
+ {...props}
80
+ >
81
+ <MinusIcon
82
+ />
83
+ </div>
84
+ )
85
+ }
70
86
 
71
87
  export { InputOTP, InputOTPGroup, InputOTPSlot, InputOTPSeparator }
@@ -0,0 +1,19 @@
1
+ import type { Meta, StoryObj } from '@storybook/react-vite';
2
+ import { Input } from "./input";
3
+
4
+ const meta = {
5
+ title: 'Design System/Input',
6
+ parameters: { layout: 'centered' },
7
+ tags: ['autodocs'],
8
+ } satisfies Meta;
9
+
10
+ export default meta;
11
+ type Story = StoryObj<typeof meta>;
12
+
13
+ export const Default: Story = {
14
+ render: () => (
15
+ <div className="p-4 w-full h-full flex items-center justify-center">
16
+ <Input type="email" placeholder="Email" />
17
+ </div>
18
+ )
19
+ };