@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.
- package/CHANGELOG.md +45 -0
- package/dist/starter/.storybook/main.ts +17 -22
- package/dist/starter/.storybook/manager-head.html +31 -31
- package/dist/starter/.storybook/manager.ts +133 -133
- package/dist/starter/.storybook/preview-head.html +12 -12
- package/dist/starter/.storybook/preview.tsx +79 -79
- package/dist/starter/biome.json +126 -0
- package/dist/starter/codegen.ts +11 -11
- package/dist/starter/components.json +27 -27
- package/dist/starter/package.json +86 -80
- package/dist/starter/public/mockServiceWorker.js +261 -261
- package/dist/starter/scripts/build-component-db.ts +17 -20
- package/dist/starter/src/App.tsx +15 -17
- package/dist/starter/src/components/ui/Accordion.stories.tsx +35 -35
- package/dist/starter/src/components/ui/Accordion.tsx +33 -33
- package/dist/starter/src/components/ui/Alert.stories.tsx +15 -15
- package/dist/starter/src/components/ui/Alert.tsx +32 -32
- package/dist/starter/src/components/ui/AlertDialog.stories.tsx +50 -50
- package/dist/starter/src/components/ui/AlertDialog.tsx +114 -115
- package/dist/starter/src/components/ui/AspectRatio.stories.tsx +20 -20
- package/dist/starter/src/components/ui/AspectRatio.tsx +1 -1
- package/dist/starter/src/components/ui/Avatar.stories.tsx +27 -27
- package/dist/starter/src/components/ui/Avatar.tsx +63 -63
- package/dist/starter/src/components/ui/Badge.stories.tsx +14 -14
- package/dist/starter/src/components/ui/Badge.tsx +27 -27
- package/dist/starter/src/components/ui/Breadcrumb.stories.tsx +38 -38
- package/dist/starter/src/components/ui/Breadcrumb.tsx +63 -62
- package/dist/starter/src/components/ui/Button.stories.tsx +55 -55
- package/dist/starter/src/components/ui/Button.tsx +49 -49
- package/dist/starter/src/components/ui/ButtonGroup.stories.tsx +17 -17
- package/dist/starter/src/components/ui/ButtonGroup.tsx +52 -53
- package/dist/starter/src/components/ui/Calendar.stories.tsx +20 -19
- package/dist/starter/src/components/ui/Calendar.tsx +142 -143
- package/dist/starter/src/components/ui/Card.stories.tsx +29 -29
- package/dist/starter/src/components/ui/Card.tsx +31 -31
- package/dist/starter/src/components/ui/Carousel.stories.tsx +41 -41
- package/dist/starter/src/components/ui/Carousel.tsx +171 -172
- package/dist/starter/src/components/ui/Chart.stories.tsx +21 -21
- package/dist/starter/src/components/ui/Chart.tsx +244 -247
- package/dist/starter/src/components/ui/Checkbox.stories.tsx +11 -11
- package/dist/starter/src/components/ui/Checkbox.tsx +18 -18
- package/dist/starter/src/components/ui/Collapsible.stories.tsx +40 -40
- package/dist/starter/src/components/ui/Collapsible.tsx +3 -3
- package/dist/starter/src/components/ui/Combobox.stories.tsx +48 -48
- package/dist/starter/src/components/ui/Combobox.tsx +204 -205
- package/dist/starter/src/components/ui/Command.stories.tsx +55 -55
- package/dist/starter/src/components/ui/Command.tsx +102 -103
- package/dist/starter/src/components/ui/ContextMenu.stories.tsx +52 -52
- package/dist/starter/src/components/ui/ContextMenu.tsx +151 -151
- package/dist/starter/src/components/ui/DesignSystem-Colors.stories.tsx +92 -92
- package/dist/starter/src/components/ui/DesignSystem-Layout.stories.tsx +139 -139
- package/dist/starter/src/components/ui/DesignSystem-Overview.stories.tsx +676 -657
- package/dist/starter/src/components/ui/DesignSystem-Typography.stories.tsx +59 -59
- package/dist/starter/src/components/ui/Dialog.stories.tsx +56 -56
- package/dist/starter/src/components/ui/Dialog.tsx +97 -98
- package/dist/starter/src/components/ui/Direction.stories.tsx +20 -20
- package/dist/starter/src/components/ui/Direction.tsx +7 -7
- package/dist/starter/src/components/ui/Drawer.stories.tsx +54 -54
- package/dist/starter/src/components/ui/Drawer.tsx +70 -70
- package/dist/starter/src/components/ui/DropdownMenu.stories.tsx +58 -58
- package/dist/starter/src/components/ui/DropdownMenu.tsx +157 -157
- package/dist/starter/src/components/ui/Empty.stories.tsx +22 -22
- package/dist/starter/src/components/ui/Empty.tsx +58 -58
- package/dist/starter/src/components/ui/Field.stories.tsx +31 -31
- package/dist/starter/src/components/ui/Field.tsx +180 -181
- package/dist/starter/src/components/ui/Form.stories.tsx +29 -29
- package/dist/starter/src/components/ui/Form.tsx +93 -96
- package/dist/starter/src/components/ui/HoverCard.stories.tsx +34 -34
- package/dist/starter/src/components/ui/HoverCard.tsx +21 -21
- package/dist/starter/src/components/ui/Input.stories.tsx +18 -18
- package/dist/starter/src/components/ui/Input.tsx +14 -14
- package/dist/starter/src/components/ui/InputGroup.stories.tsx +34 -34
- package/dist/starter/src/components/ui/InputGroup.tsx +110 -111
- package/dist/starter/src/components/ui/InputOTP.stories.tsx +28 -28
- package/dist/starter/src/components/ui/InputOTP.tsx +43 -43
- package/dist/starter/src/components/ui/Item.stories.tsx +45 -45
- package/dist/starter/src/components/ui/Item.tsx +113 -114
- package/dist/starter/src/components/ui/Kbd.stories.tsx +31 -31
- package/dist/starter/src/components/ui/Kbd.tsx +11 -11
- package/dist/starter/src/components/ui/Label.stories.tsx +62 -62
- package/dist/starter/src/components/ui/Label.tsx +26 -25
- package/dist/starter/src/components/ui/Menubar.stories.tsx +62 -62
- package/dist/starter/src/components/ui/Menubar.tsx +173 -173
- package/dist/starter/src/components/ui/NativeSelect.stories.tsx +26 -26
- package/dist/starter/src/components/ui/NativeSelect.tsx +29 -29
- package/dist/starter/src/components/ui/NavigationMenu.stories.tsx +64 -64
- package/dist/starter/src/components/ui/NavigationMenu.tsx +103 -103
- package/dist/starter/src/components/ui/Pagination.stories.tsx +61 -61
- package/dist/starter/src/components/ui/Pagination.tsx +69 -71
- package/dist/starter/src/components/ui/Popover.stories.tsx +38 -38
- package/dist/starter/src/components/ui/Popover.tsx +25 -25
- package/dist/starter/src/components/ui/Progress.stories.tsx +9 -9
- package/dist/starter/src/components/ui/Progress.tsx +14 -14
- package/dist/starter/src/components/ui/RadioGroup.stories.tsx +35 -35
- package/dist/starter/src/components/ui/RadioGroup.tsx +19 -19
- package/dist/starter/src/components/ui/Resizable.stories.tsx +54 -54
- package/dist/starter/src/components/ui/Resizable.tsx +29 -29
- package/dist/starter/src/components/ui/ScrollArea.stories.tsx +27 -27
- package/dist/starter/src/components/ui/ScrollArea.tsx +34 -34
- package/dist/starter/src/components/ui/Select.stories.tsx +43 -43
- package/dist/starter/src/components/ui/Select.tsx +120 -120
- package/dist/starter/src/components/ui/Separator.stories.tsx +27 -27
- package/dist/starter/src/components/ui/Separator.tsx +17 -17
- package/dist/starter/src/components/ui/Sheet.stories.tsx +53 -53
- package/dist/starter/src/components/ui/Sheet.tsx +69 -69
- package/dist/starter/src/components/ui/Sidebar.stories.tsx +77 -77
- package/dist/starter/src/components/ui/Sidebar.tsx +563 -564
- package/dist/starter/src/components/ui/Skeleton.stories.tsx +25 -25
- package/dist/starter/src/components/ui/Skeleton.tsx +1 -1
- package/dist/starter/src/components/ui/Slider.stories.tsx +5 -5
- package/dist/starter/src/components/ui/Slider.tsx +45 -44
- package/dist/starter/src/components/ui/Sonner.stories.tsx +32 -32
- package/dist/starter/src/components/ui/Sonner.tsx +23 -23
- package/dist/starter/src/components/ui/Spinner.stories.tsx +8 -8
- package/dist/starter/src/components/ui/Spinner.tsx +1 -1
- package/dist/starter/src/components/ui/Switch.stories.tsx +16 -17
- package/dist/starter/src/components/ui/Switch.tsx +24 -24
- package/dist/starter/src/components/ui/Table.stories.tsx +50 -50
- package/dist/starter/src/components/ui/Table.tsx +45 -45
- package/dist/starter/src/components/ui/Tabs.stories.tsx +39 -39
- package/dist/starter/src/components/ui/Tabs.tsx +47 -47
- package/dist/starter/src/components/ui/Textarea.stories.tsx +9 -9
- package/dist/starter/src/components/ui/Textarea.tsx +11 -11
- package/dist/starter/src/components/ui/Toast.stories.tsx +77 -77
- package/dist/starter/src/components/ui/Toast.tsx +75 -75
- package/dist/starter/src/components/ui/Toaster.tsx +17 -19
- package/dist/starter/src/components/ui/Toggle.stories.tsx +20 -20
- package/dist/starter/src/components/ui/Toggle.tsx +26 -26
- package/dist/starter/src/components/ui/ToggleGroup.stories.tsx +41 -41
- package/dist/starter/src/components/ui/ToggleGroup.tsx +61 -62
- package/dist/starter/src/components/ui/Tooltip.stories.tsx +26 -26
- package/dist/starter/src/components/ui/Tooltip.tsx +24 -24
- package/dist/starter/src/gql/execute.ts +1 -1
- package/dist/starter/src/gql/fragment-masking.ts +1 -1
- package/dist/starter/src/gql/graphql.ts +3 -0
- package/dist/starter/src/hooks/use-mobile.ts +11 -11
- package/dist/starter/src/hooks/use-toast.ts +135 -135
- package/dist/starter/src/index.css +105 -105
- package/dist/starter/src/lib/utils.ts +1 -1
- package/dist/starter/src/main.tsx +4 -1
- package/dist/starter/tsconfig.app.json +24 -24
- package/dist/starter/tsconfig.json +8 -8
- package/dist/starter/vite.config.ts +38 -37
- 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
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
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
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
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
|
-
|
|
26
|
-
|
|
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
|
-
|
|
29
|
-
|
|
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
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
30
|
+
className,
|
|
31
|
+
)}
|
|
32
|
+
{...props}
|
|
33
|
+
/>
|
|
34
|
+
);
|
|
36
35
|
}
|
|
37
36
|
|
|
38
37
|
const inputGroupAddonVariants = cva(
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
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
|
-
|
|
59
|
-
|
|
60
|
-
|
|
57
|
+
className,
|
|
58
|
+
align = 'inline-start',
|
|
59
|
+
...props
|
|
61
60
|
}: React.ComponentProps<'div'> & VariantProps<typeof inputGroupAddonVariants>) {
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
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
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
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
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
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
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
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
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
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
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
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
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
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,
|
|
2
|
+
import { InputOTP, InputOTPGroup, InputOTPSeparator, InputOTPSlot } from '@/components/ui/InputOTP';
|
|
3
3
|
|
|
4
4
|
const meta: Meta<typeof InputOTP> = {
|
|
5
|
-
|
|
6
|
-
|
|
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
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
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
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
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
|
-
|
|
12
|
-
|
|
13
|
-
|
|
11
|
+
className,
|
|
12
|
+
containerClassName,
|
|
13
|
+
...props
|
|
14
14
|
}: React.ComponentProps<typeof OTPInput> & {
|
|
15
|
-
|
|
15
|
+
containerClassName?: string;
|
|
16
16
|
}) {
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
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
|
-
|
|
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
|
-
|
|
35
|
-
|
|
36
|
-
|
|
34
|
+
index,
|
|
35
|
+
className,
|
|
36
|
+
...props
|
|
37
37
|
}: React.ComponentProps<'div'> & {
|
|
38
|
-
|
|
38
|
+
index: number;
|
|
39
39
|
}) {
|
|
40
|
-
|
|
41
|
-
|
|
40
|
+
const inputOTPContext = React.useContext(OTPInputContext);
|
|
41
|
+
const { char, hasFakeCaret, isActive } = inputOTPContext?.slots[index] ?? {};
|
|
42
42
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
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
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
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
|
-
|
|
7
|
-
|
|
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
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
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
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
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
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
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
|
};
|