@auto-engineer/generate-react-client 1.63.0 → 1.65.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 (144) hide show
  1. package/CHANGELOG.md +45 -0
  2. package/dist/starter/.storybook/main.ts +17 -22
  3. package/dist/starter/.storybook/manager-head.html +31 -31
  4. package/dist/starter/.storybook/manager.ts +133 -133
  5. package/dist/starter/.storybook/preview-head.html +12 -12
  6. package/dist/starter/.storybook/preview.tsx +79 -79
  7. package/dist/starter/biome.json +126 -0
  8. package/dist/starter/codegen.ts +11 -11
  9. package/dist/starter/components.json +27 -27
  10. package/dist/starter/package.json +86 -80
  11. package/dist/starter/public/mockServiceWorker.js +261 -261
  12. package/dist/starter/scripts/build-component-db.ts +17 -20
  13. package/dist/starter/src/App.tsx +15 -17
  14. package/dist/starter/src/components/ui/Accordion.stories.tsx +35 -35
  15. package/dist/starter/src/components/ui/Accordion.tsx +33 -33
  16. package/dist/starter/src/components/ui/Alert.stories.tsx +15 -15
  17. package/dist/starter/src/components/ui/Alert.tsx +32 -32
  18. package/dist/starter/src/components/ui/AlertDialog.stories.tsx +50 -50
  19. package/dist/starter/src/components/ui/AlertDialog.tsx +114 -115
  20. package/dist/starter/src/components/ui/AspectRatio.stories.tsx +20 -20
  21. package/dist/starter/src/components/ui/AspectRatio.tsx +1 -1
  22. package/dist/starter/src/components/ui/Avatar.stories.tsx +27 -27
  23. package/dist/starter/src/components/ui/Avatar.tsx +63 -63
  24. package/dist/starter/src/components/ui/Badge.stories.tsx +14 -14
  25. package/dist/starter/src/components/ui/Badge.tsx +27 -27
  26. package/dist/starter/src/components/ui/Breadcrumb.stories.tsx +38 -38
  27. package/dist/starter/src/components/ui/Breadcrumb.tsx +63 -62
  28. package/dist/starter/src/components/ui/Button.stories.tsx +55 -55
  29. package/dist/starter/src/components/ui/Button.tsx +49 -49
  30. package/dist/starter/src/components/ui/ButtonGroup.stories.tsx +17 -17
  31. package/dist/starter/src/components/ui/ButtonGroup.tsx +52 -53
  32. package/dist/starter/src/components/ui/Calendar.stories.tsx +20 -19
  33. package/dist/starter/src/components/ui/Calendar.tsx +142 -143
  34. package/dist/starter/src/components/ui/Card.stories.tsx +29 -29
  35. package/dist/starter/src/components/ui/Card.tsx +31 -31
  36. package/dist/starter/src/components/ui/Carousel.stories.tsx +41 -41
  37. package/dist/starter/src/components/ui/Carousel.tsx +171 -172
  38. package/dist/starter/src/components/ui/Chart.stories.tsx +21 -21
  39. package/dist/starter/src/components/ui/Chart.tsx +244 -247
  40. package/dist/starter/src/components/ui/Checkbox.stories.tsx +11 -11
  41. package/dist/starter/src/components/ui/Checkbox.tsx +18 -18
  42. package/dist/starter/src/components/ui/Collapsible.stories.tsx +40 -40
  43. package/dist/starter/src/components/ui/Collapsible.tsx +3 -3
  44. package/dist/starter/src/components/ui/Combobox.stories.tsx +48 -48
  45. package/dist/starter/src/components/ui/Combobox.tsx +204 -205
  46. package/dist/starter/src/components/ui/Command.stories.tsx +55 -55
  47. package/dist/starter/src/components/ui/Command.tsx +102 -103
  48. package/dist/starter/src/components/ui/ContextMenu.stories.tsx +52 -52
  49. package/dist/starter/src/components/ui/ContextMenu.tsx +151 -151
  50. package/dist/starter/src/components/ui/DesignSystem-Colors.stories.tsx +92 -92
  51. package/dist/starter/src/components/ui/DesignSystem-Layout.stories.tsx +139 -139
  52. package/dist/starter/src/components/ui/DesignSystem-Overview.stories.tsx +676 -657
  53. package/dist/starter/src/components/ui/DesignSystem-Typography.stories.tsx +59 -59
  54. package/dist/starter/src/components/ui/Dialog.stories.tsx +56 -56
  55. package/dist/starter/src/components/ui/Dialog.tsx +97 -98
  56. package/dist/starter/src/components/ui/Direction.stories.tsx +20 -20
  57. package/dist/starter/src/components/ui/Direction.tsx +7 -7
  58. package/dist/starter/src/components/ui/Drawer.stories.tsx +54 -54
  59. package/dist/starter/src/components/ui/Drawer.tsx +70 -70
  60. package/dist/starter/src/components/ui/DropdownMenu.stories.tsx +58 -58
  61. package/dist/starter/src/components/ui/DropdownMenu.tsx +157 -157
  62. package/dist/starter/src/components/ui/Empty.stories.tsx +22 -22
  63. package/dist/starter/src/components/ui/Empty.tsx +58 -58
  64. package/dist/starter/src/components/ui/Field.stories.tsx +31 -31
  65. package/dist/starter/src/components/ui/Field.tsx +180 -181
  66. package/dist/starter/src/components/ui/Form.stories.tsx +29 -29
  67. package/dist/starter/src/components/ui/Form.tsx +93 -96
  68. package/dist/starter/src/components/ui/HoverCard.stories.tsx +34 -34
  69. package/dist/starter/src/components/ui/HoverCard.tsx +21 -21
  70. package/dist/starter/src/components/ui/Input.stories.tsx +18 -18
  71. package/dist/starter/src/components/ui/Input.tsx +14 -14
  72. package/dist/starter/src/components/ui/InputGroup.stories.tsx +34 -34
  73. package/dist/starter/src/components/ui/InputGroup.tsx +110 -111
  74. package/dist/starter/src/components/ui/InputOTP.stories.tsx +28 -28
  75. package/dist/starter/src/components/ui/InputOTP.tsx +43 -43
  76. package/dist/starter/src/components/ui/Item.stories.tsx +45 -45
  77. package/dist/starter/src/components/ui/Item.tsx +113 -114
  78. package/dist/starter/src/components/ui/Kbd.stories.tsx +31 -31
  79. package/dist/starter/src/components/ui/Kbd.tsx +11 -11
  80. package/dist/starter/src/components/ui/Label.stories.tsx +62 -62
  81. package/dist/starter/src/components/ui/Label.tsx +26 -25
  82. package/dist/starter/src/components/ui/Menubar.stories.tsx +62 -62
  83. package/dist/starter/src/components/ui/Menubar.tsx +173 -173
  84. package/dist/starter/src/components/ui/NativeSelect.stories.tsx +26 -26
  85. package/dist/starter/src/components/ui/NativeSelect.tsx +29 -29
  86. package/dist/starter/src/components/ui/NavigationMenu.stories.tsx +64 -64
  87. package/dist/starter/src/components/ui/NavigationMenu.tsx +103 -103
  88. package/dist/starter/src/components/ui/Pagination.stories.tsx +61 -61
  89. package/dist/starter/src/components/ui/Pagination.tsx +69 -71
  90. package/dist/starter/src/components/ui/Popover.stories.tsx +38 -38
  91. package/dist/starter/src/components/ui/Popover.tsx +25 -25
  92. package/dist/starter/src/components/ui/Progress.stories.tsx +9 -9
  93. package/dist/starter/src/components/ui/Progress.tsx +14 -14
  94. package/dist/starter/src/components/ui/RadioGroup.stories.tsx +35 -35
  95. package/dist/starter/src/components/ui/RadioGroup.tsx +19 -19
  96. package/dist/starter/src/components/ui/Resizable.stories.tsx +54 -54
  97. package/dist/starter/src/components/ui/Resizable.tsx +29 -29
  98. package/dist/starter/src/components/ui/ScrollArea.stories.tsx +27 -27
  99. package/dist/starter/src/components/ui/ScrollArea.tsx +34 -34
  100. package/dist/starter/src/components/ui/Select.stories.tsx +43 -43
  101. package/dist/starter/src/components/ui/Select.tsx +120 -120
  102. package/dist/starter/src/components/ui/Separator.stories.tsx +27 -27
  103. package/dist/starter/src/components/ui/Separator.tsx +17 -17
  104. package/dist/starter/src/components/ui/Sheet.stories.tsx +53 -53
  105. package/dist/starter/src/components/ui/Sheet.tsx +69 -69
  106. package/dist/starter/src/components/ui/Sidebar.stories.tsx +77 -77
  107. package/dist/starter/src/components/ui/Sidebar.tsx +563 -564
  108. package/dist/starter/src/components/ui/Skeleton.stories.tsx +25 -25
  109. package/dist/starter/src/components/ui/Skeleton.tsx +1 -1
  110. package/dist/starter/src/components/ui/Slider.stories.tsx +5 -5
  111. package/dist/starter/src/components/ui/Slider.tsx +45 -44
  112. package/dist/starter/src/components/ui/Sonner.stories.tsx +32 -32
  113. package/dist/starter/src/components/ui/Sonner.tsx +23 -23
  114. package/dist/starter/src/components/ui/Spinner.stories.tsx +8 -8
  115. package/dist/starter/src/components/ui/Spinner.tsx +1 -1
  116. package/dist/starter/src/components/ui/Switch.stories.tsx +16 -17
  117. package/dist/starter/src/components/ui/Switch.tsx +24 -24
  118. package/dist/starter/src/components/ui/Table.stories.tsx +50 -50
  119. package/dist/starter/src/components/ui/Table.tsx +45 -45
  120. package/dist/starter/src/components/ui/Tabs.stories.tsx +39 -39
  121. package/dist/starter/src/components/ui/Tabs.tsx +47 -47
  122. package/dist/starter/src/components/ui/Textarea.stories.tsx +9 -9
  123. package/dist/starter/src/components/ui/Textarea.tsx +11 -11
  124. package/dist/starter/src/components/ui/Toast.stories.tsx +77 -77
  125. package/dist/starter/src/components/ui/Toast.tsx +75 -75
  126. package/dist/starter/src/components/ui/Toaster.tsx +17 -19
  127. package/dist/starter/src/components/ui/Toggle.stories.tsx +20 -20
  128. package/dist/starter/src/components/ui/Toggle.tsx +26 -26
  129. package/dist/starter/src/components/ui/ToggleGroup.stories.tsx +41 -41
  130. package/dist/starter/src/components/ui/ToggleGroup.tsx +61 -62
  131. package/dist/starter/src/components/ui/Tooltip.stories.tsx +26 -26
  132. package/dist/starter/src/components/ui/Tooltip.tsx +24 -24
  133. package/dist/starter/src/gql/execute.ts +1 -1
  134. package/dist/starter/src/gql/fragment-masking.ts +1 -1
  135. package/dist/starter/src/gql/graphql.ts +3 -0
  136. package/dist/starter/src/hooks/use-mobile.ts +11 -11
  137. package/dist/starter/src/hooks/use-toast.ts +135 -135
  138. package/dist/starter/src/index.css +105 -105
  139. package/dist/starter/src/lib/utils.ts +1 -1
  140. package/dist/starter/src/main.tsx +4 -1
  141. package/dist/starter/tsconfig.app.json +24 -24
  142. package/dist/starter/tsconfig.json +8 -8
  143. package/dist/starter/vite.config.ts +38 -37
  144. package/package.json +3 -3
@@ -1,153 +1,152 @@
1
- import * as React from 'react';
2
1
  import { cva, type VariantProps } from 'class-variance-authority';
3
-
4
- import { cn } from '@/lib/utils';
2
+ import type * as React from 'react';
5
3
  import { Button } from '@/components/ui/Button';
6
4
  import { Input } from '@/components/ui/Input';
7
5
  import { Textarea } from '@/components/ui/Textarea';
6
+ import { cn } from '@/lib/utils';
8
7
 
9
8
  /** Container that combines an input with addons like icons, buttons, or text. Supports inline and block-positioned addons. */
10
9
  function InputGroup({ className, ...props }: React.ComponentProps<'div'>) {
11
- return (
12
- <div
13
- data-slot="input-group"
14
- role="group"
15
- className={cn(
16
- 'group/input-group border-input dark:bg-input/30 relative flex w-full items-center rounded-md border shadow-xs transition-[color,box-shadow] outline-none',
17
- 'h-9 min-w-0 has-[>textarea]:h-auto',
10
+ return (
11
+ <div
12
+ data-slot="input-group"
13
+ role="group"
14
+ className={cn(
15
+ 'group/input-group border-input dark:bg-input/30 relative flex w-full items-center rounded-md border shadow-xs transition-[color,box-shadow] outline-none',
16
+ 'h-9 min-w-0 has-[>textarea]:h-auto',
18
17
 
19
- // Variants based on alignment.
20
- 'has-[>[data-align=inline-start]]:[&>input]:pl-2',
21
- 'has-[>[data-align=inline-end]]:[&>input]:pr-2',
22
- 'has-[>[data-align=block-start]]:h-auto has-[>[data-align=block-start]]:flex-col has-[>[data-align=block-start]]:[&>input]:pb-3',
23
- 'has-[>[data-align=block-end]]:h-auto has-[>[data-align=block-end]]:flex-col has-[>[data-align=block-end]]:[&>input]:pt-3',
18
+ // Variants based on alignment.
19
+ 'has-[>[data-align=inline-start]]:[&>input]:pl-2',
20
+ 'has-[>[data-align=inline-end]]:[&>input]:pr-2',
21
+ 'has-[>[data-align=block-start]]:h-auto has-[>[data-align=block-start]]:flex-col has-[>[data-align=block-start]]:[&>input]:pb-3',
22
+ 'has-[>[data-align=block-end]]:h-auto has-[>[data-align=block-end]]:flex-col has-[>[data-align=block-end]]:[&>input]:pt-3',
24
23
 
25
- // Focus state.
26
- 'has-[[data-slot=input-group-control]:focus-visible]:border-ring has-[[data-slot=input-group-control]:focus-visible]:ring-ring/50 has-[[data-slot=input-group-control]:focus-visible]:ring-[3px]',
24
+ // Focus state.
25
+ 'has-[[data-slot=input-group-control]:focus-visible]:border-ring has-[[data-slot=input-group-control]:focus-visible]:ring-ring/50 has-[[data-slot=input-group-control]:focus-visible]:ring-[3px]',
27
26
 
28
- // Error state.
29
- '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',
27
+ // Error state.
28
+ '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',
30
29
 
31
- className,
32
- )}
33
- {...props}
34
- />
35
- );
30
+ className,
31
+ )}
32
+ {...props}
33
+ />
34
+ );
36
35
  }
37
36
 
38
37
  const inputGroupAddonVariants = cva(
39
- "text-muted-foreground flex h-auto cursor-text items-center justify-center gap-2 py-1.5 text-sm font-medium select-none [&>svg:not([class*='size-'])]:size-4 [&>kbd]:rounded-[calc(var(--radius)-5px)] group-data-[disabled=true]/input-group:opacity-50",
40
- {
41
- variants: {
42
- align: {
43
- 'inline-start': 'order-first pl-3 has-[>button]:ml-[-0.45rem] has-[>kbd]:ml-[-0.35rem]',
44
- 'inline-end': 'order-last pr-3 has-[>button]:mr-[-0.45rem] has-[>kbd]:mr-[-0.35rem]',
45
- 'block-start':
46
- 'order-first w-full justify-start px-3 pt-3 [.border-b]:pb-3 group-has-[>input]/input-group:pt-2.5',
47
- 'block-end': 'order-last w-full justify-start px-3 pb-3 [.border-t]:pt-3 group-has-[>input]/input-group:pb-2.5',
48
- },
49
- },
50
- defaultVariants: {
51
- align: 'inline-start',
52
- },
53
- },
38
+ "text-muted-foreground flex h-auto cursor-text items-center justify-center gap-2 py-1.5 text-sm font-medium select-none [&>svg:not([class*='size-'])]:size-4 [&>kbd]:rounded-[calc(var(--radius)-5px)] group-data-[disabled=true]/input-group:opacity-50",
39
+ {
40
+ variants: {
41
+ align: {
42
+ 'inline-start': 'order-first pl-3 has-[>button]:ml-[-0.45rem] has-[>kbd]:ml-[-0.35rem]',
43
+ 'inline-end': 'order-last pr-3 has-[>button]:mr-[-0.45rem] has-[>kbd]:mr-[-0.35rem]',
44
+ 'block-start':
45
+ 'order-first w-full justify-start px-3 pt-3 [.border-b]:pb-3 group-has-[>input]/input-group:pt-2.5',
46
+ 'block-end': 'order-last w-full justify-start px-3 pb-3 [.border-t]:pt-3 group-has-[>input]/input-group:pb-2.5',
47
+ },
48
+ },
49
+ defaultVariants: {
50
+ align: 'inline-start',
51
+ },
52
+ },
54
53
  );
55
54
 
56
55
  /** Positioned container for icons, text, or buttons alongside the input. Use align prop to position at inline-start, inline-end, block-start, or block-end. */
57
56
  function InputGroupAddon({
58
- className,
59
- align = 'inline-start',
60
- ...props
57
+ className,
58
+ align = 'inline-start',
59
+ ...props
61
60
  }: React.ComponentProps<'div'> & VariantProps<typeof inputGroupAddonVariants>) {
62
- return (
63
- <div
64
- role="group"
65
- data-slot="input-group-addon"
66
- data-align={align}
67
- className={cn(inputGroupAddonVariants({ align }), className)}
68
- onClick={(e) => {
69
- if ((e.target as HTMLElement).closest('button')) {
70
- return;
71
- }
72
- e.currentTarget.parentElement?.querySelector('input')?.focus();
73
- }}
74
- {...props}
75
- />
76
- );
61
+ return (
62
+ // biome-ignore lint/a11y/noStaticElementInteractions lint/a11y/useKeyWithClickEvents: Click handler delegates focus to the sibling input, not an interactive action
63
+ <div
64
+ data-slot="input-group-addon"
65
+ data-align={align}
66
+ className={cn(inputGroupAddonVariants({ align }), className)}
67
+ onClick={(e) => {
68
+ if ((e.target as HTMLElement).closest('button')) {
69
+ return;
70
+ }
71
+ e.currentTarget.parentElement?.querySelector('input')?.focus();
72
+ }}
73
+ {...props}
74
+ />
75
+ );
77
76
  }
78
77
 
79
78
  const inputGroupButtonVariants = cva('text-sm shadow-none flex gap-2 items-center', {
80
- variants: {
81
- size: {
82
- xs: "h-6 gap-1 px-2 rounded-[calc(var(--radius)-5px)] [&>svg:not([class*='size-'])]:size-3.5 has-[>svg]:px-2",
83
- sm: 'h-8 px-2.5 gap-1.5 rounded-md has-[>svg]:px-2.5',
84
- 'icon-xs': 'size-6 rounded-[calc(var(--radius)-5px)] p-0 has-[>svg]:p-0',
85
- 'icon-sm': 'size-8 p-0 has-[>svg]:p-0',
86
- },
87
- },
88
- defaultVariants: {
89
- size: 'xs',
90
- },
79
+ variants: {
80
+ size: {
81
+ xs: "h-6 gap-1 px-2 rounded-[calc(var(--radius)-5px)] [&>svg:not([class*='size-'])]:size-3.5 has-[>svg]:px-2",
82
+ sm: 'h-8 px-2.5 gap-1.5 rounded-md has-[>svg]:px-2.5',
83
+ 'icon-xs': 'size-6 rounded-[calc(var(--radius)-5px)] p-0 has-[>svg]:p-0',
84
+ 'icon-sm': 'size-8 p-0 has-[>svg]:p-0',
85
+ },
86
+ },
87
+ defaultVariants: {
88
+ size: 'xs',
89
+ },
91
90
  });
92
91
 
93
92
  /** A compact button sized for use inside InputGroupAddon. */
94
93
  function InputGroupButton({
95
- className,
96
- type = 'button',
97
- variant = 'ghost',
98
- size = 'xs',
99
- ...props
94
+ className,
95
+ type = 'button',
96
+ variant = 'ghost',
97
+ size = 'xs',
98
+ ...props
100
99
  }: Omit<React.ComponentProps<typeof Button>, 'size'> & VariantProps<typeof inputGroupButtonVariants>) {
101
- return (
102
- <Button
103
- type={type}
104
- data-size={size}
105
- variant={variant}
106
- className={cn(inputGroupButtonVariants({ size }), className)}
107
- {...props}
108
- />
109
- );
100
+ return (
101
+ <Button
102
+ type={type}
103
+ data-size={size}
104
+ variant={variant}
105
+ className={cn(inputGroupButtonVariants({ size }), className)}
106
+ {...props}
107
+ />
108
+ );
110
109
  }
111
110
 
112
111
  /** Static text or icon displayed in an InputGroupAddon. */
113
112
  function InputGroupText({ className, ...props }: React.ComponentProps<'span'>) {
114
- return (
115
- <span
116
- className={cn(
117
- "text-muted-foreground flex items-center gap-2 text-sm [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4",
118
- className,
119
- )}
120
- {...props}
121
- />
122
- );
113
+ return (
114
+ <span
115
+ className={cn(
116
+ "text-muted-foreground flex items-center gap-2 text-sm [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4",
117
+ className,
118
+ )}
119
+ {...props}
120
+ />
121
+ );
123
122
  }
124
123
 
125
124
  /** Input element styled to integrate seamlessly within an InputGroup. */
126
125
  function InputGroupInput({ className, ...props }: React.ComponentProps<'input'>) {
127
- return (
128
- <Input
129
- data-slot="input-group-control"
130
- className={cn(
131
- 'flex-1 rounded-none border-0 bg-transparent shadow-none focus-visible:ring-0 dark:bg-transparent',
132
- className,
133
- )}
134
- {...props}
135
- />
136
- );
126
+ return (
127
+ <Input
128
+ data-slot="input-group-control"
129
+ className={cn(
130
+ 'flex-1 rounded-none border-0 bg-transparent shadow-none focus-visible:ring-0 dark:bg-transparent',
131
+ className,
132
+ )}
133
+ {...props}
134
+ />
135
+ );
137
136
  }
138
137
 
139
138
  /** Textarea element styled to integrate seamlessly within an InputGroup. */
140
139
  function InputGroupTextarea({ className, ...props }: React.ComponentProps<'textarea'>) {
141
- return (
142
- <Textarea
143
- data-slot="input-group-control"
144
- className={cn(
145
- 'flex-1 resize-none rounded-none border-0 bg-transparent py-3 shadow-none focus-visible:ring-0 dark:bg-transparent',
146
- className,
147
- )}
148
- {...props}
149
- />
150
- );
140
+ return (
141
+ <Textarea
142
+ data-slot="input-group-control"
143
+ className={cn(
144
+ 'flex-1 resize-none rounded-none border-0 bg-transparent py-3 shadow-none focus-visible:ring-0 dark:bg-transparent',
145
+ className,
146
+ )}
147
+ {...props}
148
+ />
149
+ );
151
150
  }
152
151
 
153
152
  export { InputGroup, InputGroupAddon, InputGroupButton, InputGroupText, InputGroupInput, InputGroupTextarea };
@@ -1,42 +1,42 @@
1
1
  import type { Meta, StoryObj } from '@storybook/react-vite';
2
- import { InputOTP, InputOTPGroup, InputOTPSlot, InputOTPSeparator } from '@/components/ui/InputOTP';
2
+ import { InputOTP, InputOTPGroup, InputOTPSeparator, InputOTPSlot } from '@/components/ui/InputOTP';
3
3
 
4
4
  const meta: Meta<typeof InputOTP> = {
5
- title: 'UI Components/InputOTP',
6
- component: InputOTP,
5
+ title: 'UI Components/InputOTP',
6
+ component: InputOTP,
7
7
  };
8
8
  export default meta;
9
9
  type Story = StoryObj<typeof InputOTP>;
10
10
 
11
11
  /** 6-digit OTP input split into two groups of three with a separator. */
12
12
  export const Default: Story = {
13
- render: () => (
14
- <InputOTP maxLength={6}>
15
- <InputOTPGroup>
16
- <InputOTPSlot index={0} />
17
- <InputOTPSlot index={1} />
18
- <InputOTPSlot index={2} />
19
- </InputOTPGroup>
20
- <InputOTPSeparator />
21
- <InputOTPGroup>
22
- <InputOTPSlot index={3} />
23
- <InputOTPSlot index={4} />
24
- <InputOTPSlot index={5} />
25
- </InputOTPGroup>
26
- </InputOTP>
27
- ),
13
+ render: () => (
14
+ <InputOTP maxLength={6}>
15
+ <InputOTPGroup>
16
+ <InputOTPSlot index={0} />
17
+ <InputOTPSlot index={1} />
18
+ <InputOTPSlot index={2} />
19
+ </InputOTPGroup>
20
+ <InputOTPSeparator />
21
+ <InputOTPGroup>
22
+ <InputOTPSlot index={3} />
23
+ <InputOTPSlot index={4} />
24
+ <InputOTPSlot index={5} />
25
+ </InputOTPGroup>
26
+ </InputOTP>
27
+ ),
28
28
  };
29
29
 
30
30
  /** Compact 4-digit PIN input without separator. */
31
31
  export const FourDigits: Story = {
32
- render: () => (
33
- <InputOTP maxLength={4}>
34
- <InputOTPGroup>
35
- <InputOTPSlot index={0} />
36
- <InputOTPSlot index={1} />
37
- <InputOTPSlot index={2} />
38
- <InputOTPSlot index={3} />
39
- </InputOTPGroup>
40
- </InputOTP>
41
- ),
32
+ render: () => (
33
+ <InputOTP maxLength={4}>
34
+ <InputOTPGroup>
35
+ <InputOTPSlot index={0} />
36
+ <InputOTPSlot index={1} />
37
+ <InputOTPSlot index={2} />
38
+ <InputOTPSlot index={3} />
39
+ </InputOTPGroup>
40
+ </InputOTP>
41
+ ),
42
42
  };
@@ -1,72 +1,72 @@
1
1
  'use client';
2
2
 
3
- import * as React from 'react';
4
3
  import { OTPInput, OTPInputContext } from 'input-otp';
5
4
  import { MinusIcon } from 'lucide-react';
5
+ import * as React from 'react';
6
6
 
7
7
  import { cn } from '@/lib/utils';
8
8
 
9
9
  /** One-time password input with individual digit slots. Handles paste, backspace, and arrow key navigation automatically. */
10
10
  function InputOTP({
11
- className,
12
- containerClassName,
13
- ...props
11
+ className,
12
+ containerClassName,
13
+ ...props
14
14
  }: React.ComponentProps<typeof OTPInput> & {
15
- containerClassName?: string;
15
+ containerClassName?: string;
16
16
  }) {
17
- return (
18
- <OTPInput
19
- data-slot="input-otp"
20
- containerClassName={cn('flex items-center gap-2 has-disabled:opacity-50', containerClassName)}
21
- className={cn('disabled:cursor-not-allowed', className)}
22
- {...props}
23
- />
24
- );
17
+ return (
18
+ <OTPInput
19
+ data-slot="input-otp"
20
+ containerClassName={cn('flex items-center gap-2 has-disabled:opacity-50', containerClassName)}
21
+ className={cn('disabled:cursor-not-allowed', className)}
22
+ {...props}
23
+ />
24
+ );
25
25
  }
26
26
 
27
27
  /** Groups InputOTPSlot elements together visually. */
28
28
  function InputOTPGroup({ className, ...props }: React.ComponentProps<'div'>) {
29
- return <div data-slot="input-otp-group" className={cn('flex items-center', className)} {...props} />;
29
+ return <div data-slot="input-otp-group" className={cn('flex items-center', className)} {...props} />;
30
30
  }
31
31
 
32
32
  /** A single digit slot in the OTP input. Shows a blinking caret when active. */
33
33
  function InputOTPSlot({
34
- index,
35
- className,
36
- ...props
34
+ index,
35
+ className,
36
+ ...props
37
37
  }: React.ComponentProps<'div'> & {
38
- index: number;
38
+ index: number;
39
39
  }) {
40
- const inputOTPContext = React.useContext(OTPInputContext);
41
- const { char, hasFakeCaret, isActive } = inputOTPContext?.slots[index] ?? {};
40
+ const inputOTPContext = React.useContext(OTPInputContext);
41
+ const { char, hasFakeCaret, isActive } = inputOTPContext?.slots[index] ?? {};
42
42
 
43
- return (
44
- <div
45
- data-slot="input-otp-slot"
46
- data-active={isActive}
47
- className={cn(
48
- 'data-[active=true]:border-ring data-[active=true]:ring-ring/50 data-[active=true]:aria-invalid:ring-destructive/20 dark:data-[active=true]:aria-invalid:ring-destructive/40 aria-invalid:border-destructive data-[active=true]:aria-invalid:border-destructive dark:bg-input/30 border-input relative flex h-9 w-9 items-center justify-center border-y border-r text-sm shadow-xs transition-all outline-none first:rounded-l-md first:border-l last:rounded-r-md data-[active=true]:z-10 data-[active=true]:ring-[3px]',
49
- className,
50
- )}
51
- {...props}
52
- >
53
- {char}
54
- {hasFakeCaret && (
55
- <div className="pointer-events-none absolute inset-0 flex items-center justify-center">
56
- <div className="animate-caret-blink bg-foreground h-4 w-px duration-1000" />
57
- </div>
58
- )}
59
- </div>
60
- );
43
+ return (
44
+ <div
45
+ data-slot="input-otp-slot"
46
+ data-active={isActive}
47
+ className={cn(
48
+ 'data-[active=true]:border-ring data-[active=true]:ring-ring/50 data-[active=true]:aria-invalid:ring-destructive/20 dark:data-[active=true]:aria-invalid:ring-destructive/40 aria-invalid:border-destructive data-[active=true]:aria-invalid:border-destructive dark:bg-input/30 border-input relative flex h-9 w-9 items-center justify-center border-y border-r text-sm shadow-xs transition-all outline-none first:rounded-l-md first:border-l last:rounded-r-md data-[active=true]:z-10 data-[active=true]:ring-[3px]',
49
+ className,
50
+ )}
51
+ {...props}
52
+ >
53
+ {char}
54
+ {hasFakeCaret && (
55
+ <div className="pointer-events-none absolute inset-0 flex items-center justify-center">
56
+ <div className="animate-caret-blink bg-foreground h-4 w-px duration-1000" />
57
+ </div>
58
+ )}
59
+ </div>
60
+ );
61
61
  }
62
62
 
63
63
  /** Visual separator between groups of OTP slots, typically a dash. */
64
64
  function InputOTPSeparator({ ...props }: React.ComponentProps<'div'>) {
65
- return (
66
- <div data-slot="input-otp-separator" role="separator" {...props}>
67
- <MinusIcon />
68
- </div>
69
- );
65
+ return (
66
+ <div data-slot="input-otp-separator" role="presentation" aria-hidden="true" {...props}>
67
+ <MinusIcon />
68
+ </div>
69
+ );
70
70
  }
71
71
 
72
72
  export { InputOTP, InputOTPGroup, InputOTPSlot, InputOTPSeparator };
@@ -1,64 +1,64 @@
1
1
  import type { Meta, StoryObj } from '@storybook/react-vite';
2
- import { Item, ItemContent, ItemTitle, ItemDescription, ItemActions } from '@/components/ui/Item';
3
2
  import { Button } from '@/components/ui/Button';
3
+ import { Item, ItemActions, ItemContent, ItemDescription, ItemTitle } from '@/components/ui/Item';
4
4
 
5
5
  const meta: Meta<typeof Item> = {
6
- title: 'UI Components/Item',
7
- component: Item,
6
+ title: 'UI Components/Item',
7
+ component: Item,
8
8
  };
9
9
  export default meta;
10
10
  type Story = StoryObj<typeof Item>;
11
11
 
12
12
  /** Standard item with title, description, and action buttons. */
13
13
  export const Default: Story = {
14
- render: () => (
15
- <Item>
16
- <ItemContent>
17
- <ItemTitle>Item Title</ItemTitle>
18
- <ItemDescription>This is a description of the item with some details.</ItemDescription>
19
- </ItemContent>
20
- <ItemActions>
21
- <Button variant="outline" size="sm">
22
- Edit
23
- </Button>
24
- <Button variant="ghost" size="sm">
25
- Delete
26
- </Button>
27
- </ItemActions>
28
- </Item>
29
- ),
14
+ render: () => (
15
+ <Item>
16
+ <ItemContent>
17
+ <ItemTitle>Item Title</ItemTitle>
18
+ <ItemDescription>This is a description of the item with some details.</ItemDescription>
19
+ </ItemContent>
20
+ <ItemActions>
21
+ <Button variant="outline" size="sm">
22
+ Edit
23
+ </Button>
24
+ <Button variant="ghost" size="sm">
25
+ Delete
26
+ </Button>
27
+ </ItemActions>
28
+ </Item>
29
+ ),
30
30
  };
31
31
 
32
32
  /** Item with visible border using the outline variant. */
33
33
  export const OutlineVariant: Story = {
34
- render: () => (
35
- <Item variant="outline">
36
- <ItemContent>
37
- <ItemTitle>Outline Item</ItemTitle>
38
- <ItemDescription>An item displayed with the outline variant and a visible border.</ItemDescription>
39
- </ItemContent>
40
- <ItemActions>
41
- <Button variant="outline" size="sm">
42
- View
43
- </Button>
44
- </ItemActions>
45
- </Item>
46
- ),
34
+ render: () => (
35
+ <Item variant="outline">
36
+ <ItemContent>
37
+ <ItemTitle>Outline Item</ItemTitle>
38
+ <ItemDescription>An item displayed with the outline variant and a visible border.</ItemDescription>
39
+ </ItemContent>
40
+ <ItemActions>
41
+ <Button variant="outline" size="sm">
42
+ View
43
+ </Button>
44
+ </ItemActions>
45
+ </Item>
46
+ ),
47
47
  };
48
48
 
49
49
  /** Compact item with reduced padding for dense lists. */
50
50
  export const SmallSize: Story = {
51
- render: () => (
52
- <Item size="sm">
53
- <ItemContent>
54
- <ItemTitle>Small Item</ItemTitle>
55
- <ItemDescription>Compact item with reduced padding.</ItemDescription>
56
- </ItemContent>
57
- <ItemActions>
58
- <Button variant="ghost" size="sm">
59
- Action
60
- </Button>
61
- </ItemActions>
62
- </Item>
63
- ),
51
+ render: () => (
52
+ <Item size="sm">
53
+ <ItemContent>
54
+ <ItemTitle>Small Item</ItemTitle>
55
+ <ItemDescription>Compact item with reduced padding.</ItemDescription>
56
+ </ItemContent>
57
+ <ItemActions>
58
+ <Button variant="ghost" size="sm">
59
+ Action
60
+ </Button>
61
+ </ItemActions>
62
+ </Item>
63
+ ),
64
64
  };