@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,148 +1,145 @@
|
|
|
1
|
-
import * as React from 'react';
|
|
2
1
|
import type { Label as LabelPrimitive } from 'radix-ui';
|
|
3
2
|
import { Slot } from 'radix-ui';
|
|
3
|
+
import * as React from 'react';
|
|
4
4
|
import {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
5
|
+
Controller,
|
|
6
|
+
type ControllerProps,
|
|
7
|
+
type FieldPath,
|
|
8
|
+
type FieldValues,
|
|
9
|
+
FormProvider,
|
|
10
|
+
useFormContext,
|
|
11
|
+
useFormState,
|
|
12
12
|
} from 'react-hook-form';
|
|
13
|
-
|
|
14
|
-
import { cn } from '@/lib/utils';
|
|
15
13
|
import { Label } from '@/components/ui/Label';
|
|
14
|
+
import { cn } from '@/lib/utils';
|
|
16
15
|
|
|
17
16
|
/**
|
|
18
17
|
* Form context provider that wraps react-hook-form's FormProvider.
|
|
19
18
|
* Use with FormField, FormItem, FormLabel, FormControl, FormDescription, and FormMessage for accessible form layouts with validation.
|
|
20
19
|
*/
|
|
21
20
|
function Form({ ...props }: React.ComponentProps<typeof FormProvider>) {
|
|
22
|
-
|
|
21
|
+
return <FormProvider {...props} />;
|
|
23
22
|
}
|
|
24
23
|
|
|
25
24
|
type FormFieldContextValue<
|
|
26
|
-
|
|
27
|
-
|
|
25
|
+
TFieldValues extends FieldValues = FieldValues,
|
|
26
|
+
TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>,
|
|
28
27
|
> = {
|
|
29
|
-
|
|
28
|
+
name: TName;
|
|
30
29
|
};
|
|
31
30
|
|
|
32
31
|
const FormFieldContext = React.createContext<FormFieldContextValue>({} as FormFieldContextValue);
|
|
33
32
|
|
|
34
33
|
/** Connects a form input to react-hook-form via Controller, providing field context to child components. */
|
|
35
34
|
function FormField<
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
>({
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
);
|
|
46
|
-
};
|
|
35
|
+
TFieldValues extends FieldValues = FieldValues,
|
|
36
|
+
TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>,
|
|
37
|
+
>({ ...props }: ControllerProps<TFieldValues, TName>) {
|
|
38
|
+
return (
|
|
39
|
+
<FormFieldContext.Provider value={{ name: props.name }}>
|
|
40
|
+
<Controller {...props} />
|
|
41
|
+
</FormFieldContext.Provider>
|
|
42
|
+
);
|
|
43
|
+
}
|
|
47
44
|
|
|
48
45
|
const useFormField = () => {
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
46
|
+
const fieldContext = React.useContext(FormFieldContext);
|
|
47
|
+
const itemContext = React.useContext(FormItemContext);
|
|
48
|
+
const { getFieldState } = useFormContext();
|
|
49
|
+
const formState = useFormState({ name: fieldContext.name });
|
|
50
|
+
const fieldState = getFieldState(fieldContext.name, formState);
|
|
51
|
+
|
|
52
|
+
if (!fieldContext) {
|
|
53
|
+
throw new Error('useFormField should be used within <FormField>');
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const { id } = itemContext;
|
|
57
|
+
|
|
58
|
+
return {
|
|
59
|
+
id,
|
|
60
|
+
name: fieldContext.name,
|
|
61
|
+
formItemId: `${id}-form-item`,
|
|
62
|
+
formDescriptionId: `${id}-form-item-description`,
|
|
63
|
+
formMessageId: `${id}-form-item-message`,
|
|
64
|
+
...fieldState,
|
|
65
|
+
};
|
|
69
66
|
};
|
|
70
67
|
|
|
71
68
|
type FormItemContextValue = {
|
|
72
|
-
|
|
69
|
+
id: string;
|
|
73
70
|
};
|
|
74
71
|
|
|
75
72
|
const FormItemContext = React.createContext<FormItemContextValue>({} as FormItemContextValue);
|
|
76
73
|
|
|
77
74
|
/** Container for a single form field that provides ID context for accessibility linking between label, input, description, and error message. */
|
|
78
75
|
function FormItem({ className, ...props }: React.ComponentProps<'div'>) {
|
|
79
|
-
|
|
76
|
+
const id = React.useId();
|
|
80
77
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
78
|
+
return (
|
|
79
|
+
<FormItemContext.Provider value={{ id }}>
|
|
80
|
+
<div data-slot="form-item" className={cn('grid gap-2', className)} {...props} />
|
|
81
|
+
</FormItemContext.Provider>
|
|
82
|
+
);
|
|
86
83
|
}
|
|
87
84
|
|
|
88
85
|
/** Label for a form field that automatically links to its input and displays error styling when validation fails. */
|
|
89
86
|
function FormLabel({ className, ...props }: React.ComponentProps<typeof LabelPrimitive.Root>) {
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
87
|
+
const { error, formItemId } = useFormField();
|
|
88
|
+
|
|
89
|
+
return (
|
|
90
|
+
<Label
|
|
91
|
+
data-slot="form-label"
|
|
92
|
+
data-error={!!error}
|
|
93
|
+
className={cn('data-[error=true]:text-destructive', className)}
|
|
94
|
+
htmlFor={formItemId}
|
|
95
|
+
{...props}
|
|
96
|
+
/>
|
|
97
|
+
);
|
|
101
98
|
}
|
|
102
99
|
|
|
103
100
|
/** Wrapper that merges accessibility attributes (id, aria-describedby, aria-invalid) onto the form input element. */
|
|
104
101
|
function FormControl({ ...props }: React.ComponentProps<typeof Slot.Root>) {
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
102
|
+
const { error, formItemId, formDescriptionId, formMessageId } = useFormField();
|
|
103
|
+
|
|
104
|
+
return (
|
|
105
|
+
<Slot.Root
|
|
106
|
+
data-slot="form-control"
|
|
107
|
+
id={formItemId}
|
|
108
|
+
aria-describedby={!error ? `${formDescriptionId}` : `${formDescriptionId} ${formMessageId}`}
|
|
109
|
+
aria-invalid={!!error}
|
|
110
|
+
{...props}
|
|
111
|
+
/>
|
|
112
|
+
);
|
|
116
113
|
}
|
|
117
114
|
|
|
118
115
|
/** Helper text displayed below a form input, linked via aria-describedby for screen readers. */
|
|
119
116
|
function FormDescription({ className, ...props }: React.ComponentProps<'p'>) {
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
117
|
+
const { formDescriptionId } = useFormField();
|
|
118
|
+
|
|
119
|
+
return (
|
|
120
|
+
<p
|
|
121
|
+
data-slot="form-description"
|
|
122
|
+
id={formDescriptionId}
|
|
123
|
+
className={cn('text-muted-foreground text-sm', className)}
|
|
124
|
+
{...props}
|
|
125
|
+
/>
|
|
126
|
+
);
|
|
130
127
|
}
|
|
131
128
|
|
|
132
129
|
/** Displays validation error messages for a form field. Automatically shows the field's error message or custom children. */
|
|
133
130
|
function FormMessage({ className, ...props }: React.ComponentProps<'p'>) {
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
131
|
+
const { error, formMessageId } = useFormField();
|
|
132
|
+
const body = error ? String(error?.message ?? '') : props.children;
|
|
133
|
+
|
|
134
|
+
if (!body) {
|
|
135
|
+
return null;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
return (
|
|
139
|
+
<p data-slot="form-message" id={formMessageId} className={cn('text-destructive text-sm', className)} {...props}>
|
|
140
|
+
{body}
|
|
141
|
+
</p>
|
|
142
|
+
);
|
|
146
143
|
}
|
|
147
144
|
|
|
148
145
|
export { useFormField, Form, FormItem, FormLabel, FormControl, FormDescription, FormMessage, FormField };
|
|
@@ -1,49 +1,49 @@
|
|
|
1
1
|
import type { Meta, StoryObj } from '@storybook/react-vite';
|
|
2
|
-
import { HoverCard, HoverCardTrigger, HoverCardContent } from '@/components/ui/HoverCard';
|
|
3
2
|
import { CalendarDaysIcon } from 'lucide-react';
|
|
3
|
+
import { HoverCard, HoverCardContent, HoverCardTrigger } from '@/components/ui/HoverCard';
|
|
4
4
|
|
|
5
5
|
const meta: Meta<typeof HoverCard> = {
|
|
6
|
-
|
|
7
|
-
|
|
6
|
+
title: 'UI Components/HoverCard',
|
|
7
|
+
component: HoverCard,
|
|
8
8
|
};
|
|
9
9
|
export default meta;
|
|
10
10
|
type Story = StoryObj<typeof HoverCard>;
|
|
11
11
|
|
|
12
12
|
/** Shows a user profile preview card triggered by hovering over a username link. */
|
|
13
13
|
export const Default: Story = {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
14
|
+
render: () => (
|
|
15
|
+
<HoverCard>
|
|
16
|
+
<HoverCardTrigger asChild>
|
|
17
|
+
<a href="#" className="text-sm font-medium underline underline-offset-4">
|
|
18
|
+
@nextjs
|
|
19
|
+
</a>
|
|
20
|
+
</HoverCardTrigger>
|
|
21
|
+
<HoverCardContent className="w-80">
|
|
22
|
+
<div className="flex justify-between space-x-4">
|
|
23
|
+
<div className="space-y-1">
|
|
24
|
+
<h4 className="text-sm font-semibold">@nextjs</h4>
|
|
25
|
+
<p className="text-sm">The React Framework for the Web. Created and maintained by @vercel.</p>
|
|
26
|
+
<div className="flex items-center pt-2">
|
|
27
|
+
<CalendarDaysIcon className="text-muted-foreground mr-2 h-4 w-4" />
|
|
28
|
+
<span className="text-muted-foreground text-xs">Joined December 2021</span>
|
|
29
|
+
</div>
|
|
30
|
+
</div>
|
|
31
|
+
</div>
|
|
32
|
+
</HoverCardContent>
|
|
33
|
+
</HoverCard>
|
|
34
|
+
),
|
|
35
35
|
};
|
|
36
36
|
|
|
37
37
|
/** Minimal hover card with just text content. */
|
|
38
38
|
export const Simple: Story = {
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
39
|
+
render: () => (
|
|
40
|
+
<HoverCard>
|
|
41
|
+
<HoverCardTrigger asChild>
|
|
42
|
+
<span className="cursor-pointer text-sm font-medium underline underline-offset-4">Hover me</span>
|
|
43
|
+
</HoverCardTrigger>
|
|
44
|
+
<HoverCardContent>
|
|
45
|
+
<p className="text-sm">This is a simple hover card with some text content.</p>
|
|
46
|
+
</HoverCardContent>
|
|
47
|
+
</HoverCard>
|
|
48
|
+
),
|
|
49
49
|
};
|
|
@@ -1,39 +1,39 @@
|
|
|
1
|
-
import * as React from 'react';
|
|
2
1
|
import { HoverCard as HoverCardPrimitive } from 'radix-ui';
|
|
2
|
+
import type * as React from 'react';
|
|
3
3
|
|
|
4
4
|
import { cn } from '@/lib/utils';
|
|
5
5
|
|
|
6
6
|
/** A popup card that appears when hovering over a trigger element. Ideal for previewing content like user profiles or link destinations. */
|
|
7
7
|
function HoverCard({ ...props }: React.ComponentProps<typeof HoverCardPrimitive.Root>) {
|
|
8
|
-
|
|
8
|
+
return <HoverCardPrimitive.Root data-slot="hover-card" {...props} />;
|
|
9
9
|
}
|
|
10
10
|
|
|
11
11
|
/** Element that activates the hover card on mouse enter. */
|
|
12
12
|
function HoverCardTrigger({ ...props }: React.ComponentProps<typeof HoverCardPrimitive.Trigger>) {
|
|
13
|
-
|
|
13
|
+
return <HoverCardPrimitive.Trigger data-slot="hover-card-trigger" {...props} />;
|
|
14
14
|
}
|
|
15
15
|
|
|
16
16
|
/** The popup content that appears when hovering. Portals to body and animates in/out. */
|
|
17
17
|
function HoverCardContent({
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
18
|
+
className,
|
|
19
|
+
align = 'center',
|
|
20
|
+
sideOffset = 4,
|
|
21
|
+
...props
|
|
22
22
|
}: React.ComponentProps<typeof HoverCardPrimitive.Content>) {
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
23
|
+
return (
|
|
24
|
+
<HoverCardPrimitive.Portal data-slot="hover-card-portal">
|
|
25
|
+
<HoverCardPrimitive.Content
|
|
26
|
+
data-slot="hover-card-content"
|
|
27
|
+
align={align}
|
|
28
|
+
sideOffset={sideOffset}
|
|
29
|
+
className={cn(
|
|
30
|
+
'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',
|
|
31
|
+
className,
|
|
32
|
+
)}
|
|
33
|
+
{...props}
|
|
34
|
+
/>
|
|
35
|
+
</HoverCardPrimitive.Portal>
|
|
36
|
+
);
|
|
37
37
|
}
|
|
38
38
|
|
|
39
39
|
export { HoverCard, HoverCardTrigger, HoverCardContent };
|
|
@@ -3,40 +3,40 @@ import { Input } from '@/components/ui/Input';
|
|
|
3
3
|
import { Label } from '@/components/ui/Label';
|
|
4
4
|
|
|
5
5
|
const meta: Meta<typeof Input> = {
|
|
6
|
-
|
|
7
|
-
|
|
6
|
+
title: 'UI Components/Input',
|
|
7
|
+
component: Input,
|
|
8
8
|
};
|
|
9
9
|
export default meta;
|
|
10
10
|
type Story = StoryObj<typeof Input>;
|
|
11
11
|
|
|
12
12
|
/** Basic empty input field. */
|
|
13
13
|
export const Default: Story = {
|
|
14
|
-
|
|
14
|
+
args: {},
|
|
15
15
|
};
|
|
16
16
|
|
|
17
17
|
/** Email input with placeholder text. */
|
|
18
18
|
export const WithPlaceholder: Story = {
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
19
|
+
args: {
|
|
20
|
+
placeholder: 'Enter your email...',
|
|
21
|
+
type: 'email',
|
|
22
|
+
},
|
|
23
23
|
};
|
|
24
24
|
|
|
25
25
|
/** Disabled input that cannot be edited. */
|
|
26
26
|
export const Disabled: Story = {
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
27
|
+
args: {
|
|
28
|
+
placeholder: 'Disabled input',
|
|
29
|
+
disabled: true,
|
|
30
|
+
defaultValue: 'Cannot edit this',
|
|
31
|
+
},
|
|
32
32
|
};
|
|
33
33
|
|
|
34
34
|
/** Input paired with a label for accessibility. */
|
|
35
35
|
export const WithLabel: Story = {
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
36
|
+
render: () => (
|
|
37
|
+
<div className="grid w-full max-w-sm items-center gap-1.5">
|
|
38
|
+
<Label htmlFor="email-input">Email</Label>
|
|
39
|
+
<Input type="email" id="email-input" placeholder="Email" />
|
|
40
|
+
</div>
|
|
41
|
+
),
|
|
42
42
|
};
|
|
@@ -1,22 +1,22 @@
|
|
|
1
|
-
import * as React from 'react';
|
|
1
|
+
import type * as React from 'react';
|
|
2
2
|
|
|
3
3
|
import { cn } from '@/lib/utils';
|
|
4
4
|
|
|
5
5
|
/** A styled text input field. Supports all native input types including file uploads. Shows validation error styling via aria-invalid. */
|
|
6
6
|
function Input({ className, type, ...props }: React.ComponentProps<'input'>) {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
7
|
+
return (
|
|
8
|
+
<input
|
|
9
|
+
type={type}
|
|
10
|
+
data-slot="input"
|
|
11
|
+
className={cn(
|
|
12
|
+
'file:text-foreground placeholder:text-muted-foreground selection:bg-primary selection:text-primary-foreground dark:bg-input/30 border-input h-9 w-full min-w-0 rounded-md border bg-transparent px-3 py-1 text-base shadow-xs transition-[color,box-shadow] outline-none file:inline-flex file:h-7 file:border-0 file:bg-transparent file:text-sm file:font-medium disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 md:text-sm',
|
|
13
|
+
'focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px]',
|
|
14
|
+
'aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive',
|
|
15
|
+
className,
|
|
16
|
+
)}
|
|
17
|
+
{...props}
|
|
18
|
+
/>
|
|
19
|
+
);
|
|
20
20
|
}
|
|
21
21
|
|
|
22
22
|
export { Input };
|
|
@@ -1,53 +1,53 @@
|
|
|
1
1
|
import type { Meta, StoryObj } from '@storybook/react-vite';
|
|
2
|
-
import { InputGroup, InputGroupAddon, InputGroupText, InputGroupInput } from '@/components/ui/InputGroup';
|
|
3
2
|
import { MailIcon, SearchIcon } from 'lucide-react';
|
|
3
|
+
import { InputGroup, InputGroupAddon, InputGroupInput, InputGroupText } from '@/components/ui/InputGroup';
|
|
4
4
|
|
|
5
5
|
const meta: Meta<typeof InputGroup> = {
|
|
6
|
-
|
|
7
|
-
|
|
6
|
+
title: 'UI Components/InputGroup',
|
|
7
|
+
component: InputGroup,
|
|
8
8
|
};
|
|
9
9
|
export default meta;
|
|
10
10
|
type Story = StoryObj<typeof InputGroup>;
|
|
11
11
|
|
|
12
12
|
/** Input with a leading email icon addon. */
|
|
13
13
|
export const Default: Story = {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
14
|
+
render: () => (
|
|
15
|
+
<InputGroup className="max-w-sm">
|
|
16
|
+
<InputGroupAddon align="inline-start">
|
|
17
|
+
<InputGroupText>
|
|
18
|
+
<MailIcon />
|
|
19
|
+
</InputGroupText>
|
|
20
|
+
</InputGroupAddon>
|
|
21
|
+
<InputGroupInput placeholder="you@example.com" />
|
|
22
|
+
</InputGroup>
|
|
23
|
+
),
|
|
24
24
|
};
|
|
25
25
|
|
|
26
26
|
/** URL input with text addons on both sides. */
|
|
27
27
|
export const WithPrefixAndSuffix: Story = {
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
28
|
+
render: () => (
|
|
29
|
+
<InputGroup className="max-w-sm">
|
|
30
|
+
<InputGroupAddon align="inline-start">
|
|
31
|
+
<InputGroupText>https://</InputGroupText>
|
|
32
|
+
</InputGroupAddon>
|
|
33
|
+
<InputGroupInput placeholder="example.com" />
|
|
34
|
+
<InputGroupAddon align="inline-end">
|
|
35
|
+
<InputGroupText>.com</InputGroupText>
|
|
36
|
+
</InputGroupAddon>
|
|
37
|
+
</InputGroup>
|
|
38
|
+
),
|
|
39
39
|
};
|
|
40
40
|
|
|
41
41
|
/** Search input with a magnifying glass icon. */
|
|
42
42
|
export const SearchInput: Story = {
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
43
|
+
render: () => (
|
|
44
|
+
<InputGroup className="max-w-sm">
|
|
45
|
+
<InputGroupAddon align="inline-start">
|
|
46
|
+
<InputGroupText>
|
|
47
|
+
<SearchIcon />
|
|
48
|
+
</InputGroupText>
|
|
49
|
+
</InputGroupAddon>
|
|
50
|
+
<InputGroupInput placeholder="Search..." />
|
|
51
|
+
</InputGroup>
|
|
52
|
+
),
|
|
53
53
|
};
|