@cyberbloxai/ui-kit 0.1.2 → 0.2.1
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/README.md +30 -1
- package/package.json +6 -2
- package/src/App.css +42 -0
- package/src/App.tsx +31 -0
- package/src/cli.js +393 -0
- package/src/components/CodeBlock.tsx +50 -0
- package/src/components/ComponentLivePreview.tsx +310 -0
- package/src/components/NavLink.tsx +28 -0
- package/src/components/PropTable.tsx +54 -0
- package/src/components/showcase/AuthShowcase.tsx +164 -0
- package/src/components/showcase/ComponentsShowcase.tsx +133 -0
- package/src/components/showcase/DashboardShowcase.tsx +153 -0
- package/src/components/showcase/ErrorShowcase.tsx +55 -0
- package/src/components/showcase/NotificationShowcase.tsx +102 -0
- package/src/components/ui/accordion.tsx +52 -0
- package/src/components/ui/alert-dialog.tsx +104 -0
- package/src/components/ui/alert.tsx +43 -0
- package/src/components/ui/aspect-ratio.tsx +5 -0
- package/src/components/ui/avatar.tsx +38 -0
- package/src/components/ui/badge.tsx +29 -0
- package/src/components/ui/breadcrumb.tsx +90 -0
- package/src/components/ui/button.tsx +47 -0
- package/src/components/ui/calendar.tsx +54 -0
- package/src/components/ui/card.tsx +43 -0
- package/src/components/ui/carousel.tsx +224 -0
- package/src/components/ui/chart.tsx +303 -0
- package/src/components/ui/checkbox.tsx +26 -0
- package/src/components/ui/collapsible.tsx +9 -0
- package/src/components/ui/command.tsx +132 -0
- package/src/components/ui/context-menu.tsx +178 -0
- package/src/components/ui/dialog.tsx +95 -0
- package/src/components/ui/drawer.tsx +87 -0
- package/src/components/ui/dropdown-menu.tsx +179 -0
- package/src/components/ui/form.tsx +129 -0
- package/src/components/ui/hover-card.tsx +27 -0
- package/src/components/ui/input-otp.tsx +61 -0
- package/src/components/ui/input.tsx +22 -0
- package/src/components/ui/label.tsx +17 -0
- package/src/components/ui/menubar.tsx +207 -0
- package/src/components/ui/navigation-menu.tsx +120 -0
- package/src/components/ui/pagination.tsx +81 -0
- package/src/components/ui/popover.tsx +29 -0
- package/src/components/ui/progress.tsx +23 -0
- package/src/components/ui/radio-group.tsx +36 -0
- package/src/components/ui/resizable.tsx +37 -0
- package/src/components/ui/scroll-area.tsx +38 -0
- package/src/components/ui/select.tsx +143 -0
- package/src/components/ui/separator.tsx +20 -0
- package/src/components/ui/sheet.tsx +107 -0
- package/src/components/ui/sidebar.tsx +637 -0
- package/src/components/ui/skeleton.tsx +7 -0
- package/src/components/ui/slider.tsx +23 -0
- package/src/components/ui/sonner.tsx +27 -0
- package/src/components/ui/switch.tsx +27 -0
- package/src/components/ui/table.tsx +72 -0
- package/src/components/ui/tabs.tsx +53 -0
- package/src/components/ui/textarea.tsx +21 -0
- package/src/components/ui/toast.tsx +111 -0
- package/src/components/ui/toaster.tsx +24 -0
- package/src/components/ui/toggle-group.tsx +49 -0
- package/src/components/ui/toggle.tsx +37 -0
- package/src/components/ui/tooltip.tsx +28 -0
- package/src/components/ui/use-toast.ts +3 -0
- package/src/data/componentRegistry.ts +501 -0
- package/src/hooks/use-mobile.tsx +19 -0
- package/src/hooks/use-toast.ts +186 -0
- package/src/index.css +105 -0
- package/src/lib/index.ts +58 -0
- package/src/lib/utils.ts +6 -0
- package/src/main.tsx +5 -0
- package/src/pages/ComponentDetail.tsx +167 -0
- package/src/pages/ComponentsList.tsx +126 -0
- package/src/pages/Index.tsx +223 -0
- package/src/pages/NotFound.tsx +24 -0
- package/src/test/example.test.ts +7 -0
- package/src/test/setup.ts +15 -0
- package/src/vite-env.d.ts +1 -0
|
@@ -0,0 +1,310 @@
|
|
|
1
|
+
import { useState } from "react";
|
|
2
|
+
import { Button } from "@/components/ui/button";
|
|
3
|
+
import { Input } from "@/components/ui/input";
|
|
4
|
+
import { Badge } from "@/components/ui/badge";
|
|
5
|
+
import { Switch } from "@/components/ui/switch";
|
|
6
|
+
import { Slider } from "@/components/ui/slider";
|
|
7
|
+
import { Checkbox } from "@/components/ui/checkbox";
|
|
8
|
+
import { Progress } from "@/components/ui/progress";
|
|
9
|
+
import { Avatar, AvatarFallback } from "@/components/ui/avatar";
|
|
10
|
+
import { Textarea } from "@/components/ui/textarea";
|
|
11
|
+
import { Skeleton } from "@/components/ui/skeleton";
|
|
12
|
+
import { Label } from "@/components/ui/label";
|
|
13
|
+
import {
|
|
14
|
+
Dialog, DialogTrigger, DialogContent,
|
|
15
|
+
DialogHeader, DialogTitle, DialogDescription,
|
|
16
|
+
DialogFooter, DialogClose,
|
|
17
|
+
} from "@/components/ui/dialog";
|
|
18
|
+
import {
|
|
19
|
+
Drawer, DrawerTrigger, DrawerContent,
|
|
20
|
+
DrawerHeader, DrawerTitle, DrawerDescription,
|
|
21
|
+
DrawerFooter, DrawerClose,
|
|
22
|
+
} from "@/components/ui/drawer";
|
|
23
|
+
import { Tabs, TabsList, TabsTrigger, TabsContent } from "@/components/ui/tabs";
|
|
24
|
+
import {
|
|
25
|
+
Select, SelectTrigger, SelectValue, SelectContent,
|
|
26
|
+
SelectItem, SelectGroup, SelectLabel,
|
|
27
|
+
} from "@/components/ui/select";
|
|
28
|
+
import {
|
|
29
|
+
Accordion, AccordionItem, AccordionTrigger, AccordionContent,
|
|
30
|
+
} from "@/components/ui/accordion";
|
|
31
|
+
import {
|
|
32
|
+
Tooltip, TooltipTrigger, TooltipContent, TooltipProvider,
|
|
33
|
+
} from "@/components/ui/tooltip";
|
|
34
|
+
import { Loader2, ArrowRight, Plus, Settings, Info } from "lucide-react";
|
|
35
|
+
|
|
36
|
+
interface Props {
|
|
37
|
+
componentId: string;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const ComponentLivePreview = ({ componentId }: Props) => {
|
|
41
|
+
const [sliderVal, setSliderVal] = useState([50]);
|
|
42
|
+
|
|
43
|
+
switch (componentId) {
|
|
44
|
+
case "button":
|
|
45
|
+
return (
|
|
46
|
+
<div className="flex flex-wrap gap-3">
|
|
47
|
+
<Button>Primary</Button>
|
|
48
|
+
<Button variant="secondary">Secondary</Button>
|
|
49
|
+
<Button variant="outline">Outline</Button>
|
|
50
|
+
<Button variant="destructive">Destructive</Button>
|
|
51
|
+
<Button variant="ghost">Ghost</Button>
|
|
52
|
+
<Button variant="link">Link</Button>
|
|
53
|
+
<Button disabled><Loader2 className="mr-2 h-4 w-4 animate-spin" />Loading</Button>
|
|
54
|
+
<Button size="icon"><ArrowRight /></Button>
|
|
55
|
+
</div>
|
|
56
|
+
);
|
|
57
|
+
|
|
58
|
+
case "input":
|
|
59
|
+
return (
|
|
60
|
+
<div className="space-y-3 max-w-sm">
|
|
61
|
+
<Input placeholder="Default input" />
|
|
62
|
+
<Input type="email" placeholder="Email input" />
|
|
63
|
+
<Input placeholder="Disabled" disabled />
|
|
64
|
+
</div>
|
|
65
|
+
);
|
|
66
|
+
|
|
67
|
+
case "badge":
|
|
68
|
+
return (
|
|
69
|
+
<div className="flex flex-wrap gap-2">
|
|
70
|
+
<Badge>Default</Badge>
|
|
71
|
+
<Badge variant="secondary">Secondary</Badge>
|
|
72
|
+
<Badge variant="destructive">Destructive</Badge>
|
|
73
|
+
<Badge variant="outline">Outline</Badge>
|
|
74
|
+
</div>
|
|
75
|
+
);
|
|
76
|
+
|
|
77
|
+
case "switch":
|
|
78
|
+
return (
|
|
79
|
+
<div className="space-y-3">
|
|
80
|
+
<div className="flex items-center gap-3">
|
|
81
|
+
<Switch id="s1" />
|
|
82
|
+
<Label htmlFor="s1">Off by default</Label>
|
|
83
|
+
</div>
|
|
84
|
+
<div className="flex items-center gap-3">
|
|
85
|
+
<Switch id="s2" defaultChecked />
|
|
86
|
+
<Label htmlFor="s2">On by default</Label>
|
|
87
|
+
</div>
|
|
88
|
+
</div>
|
|
89
|
+
);
|
|
90
|
+
|
|
91
|
+
case "slider":
|
|
92
|
+
return (
|
|
93
|
+
<div className="space-y-4 max-w-sm">
|
|
94
|
+
<div className="space-y-2">
|
|
95
|
+
<div className="flex justify-between text-xs text-muted-foreground">
|
|
96
|
+
<span>Volume</span>
|
|
97
|
+
<span>{sliderVal[0]}%</span>
|
|
98
|
+
</div>
|
|
99
|
+
<Slider value={sliderVal} onValueChange={setSliderVal} max={100} step={1} />
|
|
100
|
+
</div>
|
|
101
|
+
</div>
|
|
102
|
+
);
|
|
103
|
+
|
|
104
|
+
case "checkbox":
|
|
105
|
+
return (
|
|
106
|
+
<div className="space-y-3">
|
|
107
|
+
{["Design system", "Components", "Documentation"].map((label) => (
|
|
108
|
+
<div key={label} className="flex items-center gap-2">
|
|
109
|
+
<Checkbox id={label} />
|
|
110
|
+
<Label htmlFor={label}>{label}</Label>
|
|
111
|
+
</div>
|
|
112
|
+
))}
|
|
113
|
+
</div>
|
|
114
|
+
);
|
|
115
|
+
|
|
116
|
+
case "progress":
|
|
117
|
+
return (
|
|
118
|
+
<div className="space-y-4 max-w-sm">
|
|
119
|
+
<Progress value={30} />
|
|
120
|
+
<Progress value={60} />
|
|
121
|
+
<Progress value={90} />
|
|
122
|
+
</div>
|
|
123
|
+
);
|
|
124
|
+
|
|
125
|
+
case "avatar":
|
|
126
|
+
return (
|
|
127
|
+
<div className="flex items-center gap-4">
|
|
128
|
+
<div className="flex -space-x-2">
|
|
129
|
+
{["AK", "BR", "CS", "DW"].map((i) => (
|
|
130
|
+
<Avatar key={i} className="h-10 w-10 border-2 border-background">
|
|
131
|
+
<AvatarFallback className="bg-primary/20 text-primary text-xs">{i}</AvatarFallback>
|
|
132
|
+
</Avatar>
|
|
133
|
+
))}
|
|
134
|
+
</div>
|
|
135
|
+
<Avatar className="h-14 w-14">
|
|
136
|
+
<AvatarFallback className="bg-accent/20 text-accent text-lg">JD</AvatarFallback>
|
|
137
|
+
</Avatar>
|
|
138
|
+
</div>
|
|
139
|
+
);
|
|
140
|
+
|
|
141
|
+
case "textarea":
|
|
142
|
+
return (
|
|
143
|
+
<div className="space-y-2 max-w-sm">
|
|
144
|
+
<Label htmlFor="bio">Bio</Label>
|
|
145
|
+
<Textarea id="bio" placeholder="Tell us about yourself..." rows={4} />
|
|
146
|
+
</div>
|
|
147
|
+
);
|
|
148
|
+
|
|
149
|
+
case "skeleton":
|
|
150
|
+
return (
|
|
151
|
+
<div className="flex items-center gap-4">
|
|
152
|
+
<Skeleton className="h-12 w-12 rounded-full" />
|
|
153
|
+
<div className="space-y-2">
|
|
154
|
+
<Skeleton className="h-4 w-[200px]" />
|
|
155
|
+
<Skeleton className="h-4 w-[150px]" />
|
|
156
|
+
</div>
|
|
157
|
+
</div>
|
|
158
|
+
);
|
|
159
|
+
|
|
160
|
+
case "label":
|
|
161
|
+
return (
|
|
162
|
+
<div className="space-y-2 max-w-sm">
|
|
163
|
+
<Label htmlFor="demo">Full Name</Label>
|
|
164
|
+
<Input id="demo" placeholder="John Doe" />
|
|
165
|
+
</div>
|
|
166
|
+
);
|
|
167
|
+
|
|
168
|
+
case "dialog":
|
|
169
|
+
return (
|
|
170
|
+
<Dialog>
|
|
171
|
+
<DialogTrigger asChild>
|
|
172
|
+
<Button variant="outline">Open Dialog</Button>
|
|
173
|
+
</DialogTrigger>
|
|
174
|
+
<DialogContent>
|
|
175
|
+
<DialogHeader>
|
|
176
|
+
<DialogTitle>Are you sure?</DialogTitle>
|
|
177
|
+
<DialogDescription>This action cannot be undone.</DialogDescription>
|
|
178
|
+
</DialogHeader>
|
|
179
|
+
<DialogFooter>
|
|
180
|
+
<DialogClose asChild>
|
|
181
|
+
<Button variant="outline">Cancel</Button>
|
|
182
|
+
</DialogClose>
|
|
183
|
+
<Button>Confirm</Button>
|
|
184
|
+
</DialogFooter>
|
|
185
|
+
</DialogContent>
|
|
186
|
+
</Dialog>
|
|
187
|
+
);
|
|
188
|
+
|
|
189
|
+
case "drawer":
|
|
190
|
+
return (
|
|
191
|
+
<Drawer>
|
|
192
|
+
<DrawerTrigger asChild>
|
|
193
|
+
<Button variant="outline">Open Drawer</Button>
|
|
194
|
+
</DrawerTrigger>
|
|
195
|
+
<DrawerContent>
|
|
196
|
+
<DrawerHeader>
|
|
197
|
+
<DrawerTitle>Settings</DrawerTitle>
|
|
198
|
+
<DrawerDescription>Adjust your preferences below.</DrawerDescription>
|
|
199
|
+
</DrawerHeader>
|
|
200
|
+
<div className="p-4 space-y-3">
|
|
201
|
+
<div className="flex items-center justify-between">
|
|
202
|
+
<Label>Notifications</Label>
|
|
203
|
+
<Switch />
|
|
204
|
+
</div>
|
|
205
|
+
<div className="flex items-center justify-between">
|
|
206
|
+
<Label>Dark Mode</Label>
|
|
207
|
+
<Switch defaultChecked />
|
|
208
|
+
</div>
|
|
209
|
+
</div>
|
|
210
|
+
<DrawerFooter>
|
|
211
|
+
<Button>Save Changes</Button>
|
|
212
|
+
<DrawerClose asChild>
|
|
213
|
+
<Button variant="outline">Cancel</Button>
|
|
214
|
+
</DrawerClose>
|
|
215
|
+
</DrawerFooter>
|
|
216
|
+
</DrawerContent>
|
|
217
|
+
</Drawer>
|
|
218
|
+
);
|
|
219
|
+
|
|
220
|
+
case "tabs":
|
|
221
|
+
return (
|
|
222
|
+
<Tabs defaultValue="overview" className="w-full max-w-md">
|
|
223
|
+
<TabsList className="grid w-full grid-cols-3">
|
|
224
|
+
<TabsTrigger value="overview">Overview</TabsTrigger>
|
|
225
|
+
<TabsTrigger value="analytics">Analytics</TabsTrigger>
|
|
226
|
+
<TabsTrigger value="settings">Settings</TabsTrigger>
|
|
227
|
+
</TabsList>
|
|
228
|
+
<TabsContent value="overview" className="mt-3 text-sm text-muted-foreground">
|
|
229
|
+
<p>Here's a quick summary of your project status and recent activity.</p>
|
|
230
|
+
</TabsContent>
|
|
231
|
+
<TabsContent value="analytics" className="mt-3 text-sm text-muted-foreground">
|
|
232
|
+
<p>View charts and metrics about your usage and performance.</p>
|
|
233
|
+
</TabsContent>
|
|
234
|
+
<TabsContent value="settings" className="mt-3 text-sm text-muted-foreground">
|
|
235
|
+
<p>Configure notifications, integrations, and account preferences.</p>
|
|
236
|
+
</TabsContent>
|
|
237
|
+
</Tabs>
|
|
238
|
+
);
|
|
239
|
+
|
|
240
|
+
case "select":
|
|
241
|
+
return (
|
|
242
|
+
<div className="space-y-3 max-w-[220px]">
|
|
243
|
+
<Label>Favorite fruit</Label>
|
|
244
|
+
<Select>
|
|
245
|
+
<SelectTrigger>
|
|
246
|
+
<SelectValue placeholder="Select a fruit" />
|
|
247
|
+
</SelectTrigger>
|
|
248
|
+
<SelectContent>
|
|
249
|
+
<SelectGroup>
|
|
250
|
+
<SelectLabel>Fruits</SelectLabel>
|
|
251
|
+
<SelectItem value="apple">Apple</SelectItem>
|
|
252
|
+
<SelectItem value="banana">Banana</SelectItem>
|
|
253
|
+
<SelectItem value="orange">Orange</SelectItem>
|
|
254
|
+
<SelectItem value="grape">Grape</SelectItem>
|
|
255
|
+
</SelectGroup>
|
|
256
|
+
</SelectContent>
|
|
257
|
+
</Select>
|
|
258
|
+
</div>
|
|
259
|
+
);
|
|
260
|
+
|
|
261
|
+
case "accordion":
|
|
262
|
+
return (
|
|
263
|
+
<Accordion type="single" collapsible className="w-full max-w-md">
|
|
264
|
+
<AccordionItem value="item-1">
|
|
265
|
+
<AccordionTrigger>Is it accessible?</AccordionTrigger>
|
|
266
|
+
<AccordionContent>Yes. It adheres to the WAI-ARIA design pattern.</AccordionContent>
|
|
267
|
+
</AccordionItem>
|
|
268
|
+
<AccordionItem value="item-2">
|
|
269
|
+
<AccordionTrigger>Is it styled?</AccordionTrigger>
|
|
270
|
+
<AccordionContent>Yes. It comes with default styles that match the design system.</AccordionContent>
|
|
271
|
+
</AccordionItem>
|
|
272
|
+
<AccordionItem value="item-3">
|
|
273
|
+
<AccordionTrigger>Is it animated?</AccordionTrigger>
|
|
274
|
+
<AccordionContent>Yes. It uses CSS animations for smooth open and close transitions.</AccordionContent>
|
|
275
|
+
</AccordionItem>
|
|
276
|
+
</Accordion>
|
|
277
|
+
);
|
|
278
|
+
|
|
279
|
+
case "tooltip":
|
|
280
|
+
return (
|
|
281
|
+
<TooltipProvider>
|
|
282
|
+
<div className="flex flex-wrap gap-3">
|
|
283
|
+
<Tooltip>
|
|
284
|
+
<TooltipTrigger asChild>
|
|
285
|
+
<Button variant="outline" size="icon"><Plus className="h-4 w-4" /></Button>
|
|
286
|
+
</TooltipTrigger>
|
|
287
|
+
<TooltipContent><p>Add item</p></TooltipContent>
|
|
288
|
+
</Tooltip>
|
|
289
|
+
<Tooltip>
|
|
290
|
+
<TooltipTrigger asChild>
|
|
291
|
+
<Button variant="outline" size="icon"><Settings className="h-4 w-4" /></Button>
|
|
292
|
+
</TooltipTrigger>
|
|
293
|
+
<TooltipContent side="bottom"><p>Settings</p></TooltipContent>
|
|
294
|
+
</Tooltip>
|
|
295
|
+
<Tooltip>
|
|
296
|
+
<TooltipTrigger asChild>
|
|
297
|
+
<Button variant="outline" size="icon"><Info className="h-4 w-4" /></Button>
|
|
298
|
+
</TooltipTrigger>
|
|
299
|
+
<TooltipContent side="right"><p>More info</p></TooltipContent>
|
|
300
|
+
</Tooltip>
|
|
301
|
+
</div>
|
|
302
|
+
</TooltipProvider>
|
|
303
|
+
);
|
|
304
|
+
|
|
305
|
+
default:
|
|
306
|
+
return <p className="text-muted-foreground text-sm">No preview available.</p>;
|
|
307
|
+
}
|
|
308
|
+
};
|
|
309
|
+
|
|
310
|
+
export default ComponentLivePreview;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { NavLink as RouterNavLink, NavLinkProps } from "react-router-dom";
|
|
2
|
+
import { forwardRef } from "react";
|
|
3
|
+
import { cn } from "@/lib/utils";
|
|
4
|
+
|
|
5
|
+
interface NavLinkCompatProps extends Omit<NavLinkProps, "className"> {
|
|
6
|
+
className?: string;
|
|
7
|
+
activeClassName?: string;
|
|
8
|
+
pendingClassName?: string;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const NavLink = forwardRef<HTMLAnchorElement, NavLinkCompatProps>(
|
|
12
|
+
({ className, activeClassName, pendingClassName, to, ...props }, ref) => {
|
|
13
|
+
return (
|
|
14
|
+
<RouterNavLink
|
|
15
|
+
ref={ref}
|
|
16
|
+
to={to}
|
|
17
|
+
className={({ isActive, isPending }) =>
|
|
18
|
+
cn(className, isActive && activeClassName, isPending && pendingClassName)
|
|
19
|
+
}
|
|
20
|
+
{...props}
|
|
21
|
+
/>
|
|
22
|
+
);
|
|
23
|
+
},
|
|
24
|
+
);
|
|
25
|
+
|
|
26
|
+
NavLink.displayName = "NavLink";
|
|
27
|
+
|
|
28
|
+
export { NavLink };
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import type { PropDoc } from "@/data/componentRegistry";
|
|
2
|
+
import { Badge } from "@/components/ui/badge";
|
|
3
|
+
|
|
4
|
+
interface PropTableProps {
|
|
5
|
+
props: PropDoc[];
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
const PropTable = ({ props }: PropTableProps) => {
|
|
9
|
+
return (
|
|
10
|
+
<div className="overflow-x-auto rounded-lg border border-border/40">
|
|
11
|
+
<table className="w-full text-sm">
|
|
12
|
+
<thead>
|
|
13
|
+
<tr className="bg-muted/60 border-b border-border/40">
|
|
14
|
+
<th className="text-left px-4 py-3 font-medium text-muted-foreground">Prop</th>
|
|
15
|
+
<th className="text-left px-4 py-3 font-medium text-muted-foreground">Type</th>
|
|
16
|
+
<th className="text-left px-4 py-3 font-medium text-muted-foreground hidden sm:table-cell">Default</th>
|
|
17
|
+
<th className="text-left px-4 py-3 font-medium text-muted-foreground hidden md:table-cell">Description</th>
|
|
18
|
+
</tr>
|
|
19
|
+
</thead>
|
|
20
|
+
<tbody>
|
|
21
|
+
{props.map((prop) => (
|
|
22
|
+
<tr key={prop.name} className="border-b border-border/20 hover:bg-muted/20 transition-colors">
|
|
23
|
+
<td className="px-4 py-3">
|
|
24
|
+
<code className="text-xs font-mono text-primary bg-primary/10 px-1.5 py-0.5 rounded">
|
|
25
|
+
{prop.name}
|
|
26
|
+
</code>
|
|
27
|
+
{prop.required && (
|
|
28
|
+
<Badge variant="destructive" className="ml-2 text-[10px] px-1.5 py-0">
|
|
29
|
+
required
|
|
30
|
+
</Badge>
|
|
31
|
+
)}
|
|
32
|
+
</td>
|
|
33
|
+
<td className="px-4 py-3">
|
|
34
|
+
<code className="text-xs font-mono text-muted-foreground break-all">{prop.type}</code>
|
|
35
|
+
</td>
|
|
36
|
+
<td className="px-4 py-3 hidden sm:table-cell">
|
|
37
|
+
{prop.default ? (
|
|
38
|
+
<code className="text-xs font-mono text-foreground/70">{prop.default}</code>
|
|
39
|
+
) : (
|
|
40
|
+
<span className="text-xs text-muted-foreground">—</span>
|
|
41
|
+
)}
|
|
42
|
+
</td>
|
|
43
|
+
<td className="px-4 py-3 text-xs text-muted-foreground hidden md:table-cell">
|
|
44
|
+
{prop.description}
|
|
45
|
+
</td>
|
|
46
|
+
</tr>
|
|
47
|
+
))}
|
|
48
|
+
</tbody>
|
|
49
|
+
</table>
|
|
50
|
+
</div>
|
|
51
|
+
);
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
export default PropTable;
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
import { useState } from "react";
|
|
2
|
+
import { motion } from "framer-motion";
|
|
3
|
+
import { Mail, Lock, Eye, EyeOff, Github, ArrowRight, Loader2, User, KeyRound } from "lucide-react";
|
|
4
|
+
import { Input } from "@/components/ui/input";
|
|
5
|
+
import { Button } from "@/components/ui/button";
|
|
6
|
+
import { Label } from "@/components/ui/label";
|
|
7
|
+
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
|
|
8
|
+
|
|
9
|
+
const AuthShowcase = () => {
|
|
10
|
+
const [showPassword, setShowPassword] = useState(false);
|
|
11
|
+
const [loading, setLoading] = useState(false);
|
|
12
|
+
const [activeTab, setActiveTab] = useState("login");
|
|
13
|
+
|
|
14
|
+
const handleSubmit = (e: React.FormEvent) => {
|
|
15
|
+
e.preventDefault();
|
|
16
|
+
setLoading(true);
|
|
17
|
+
setTimeout(() => setLoading(false), 2000);
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
return (
|
|
21
|
+
<div className="w-full max-w-md mx-auto">
|
|
22
|
+
<Tabs value={activeTab} onValueChange={setActiveTab} className="w-full">
|
|
23
|
+
<TabsList className="grid w-full grid-cols-3 bg-muted/50 mb-6">
|
|
24
|
+
<TabsTrigger value="login">Login</TabsTrigger>
|
|
25
|
+
<TabsTrigger value="signup">Sign Up</TabsTrigger>
|
|
26
|
+
<TabsTrigger value="forgot">Forgot</TabsTrigger>
|
|
27
|
+
</TabsList>
|
|
28
|
+
|
|
29
|
+
<TabsContent value="login">
|
|
30
|
+
<motion.form
|
|
31
|
+
initial={{ opacity: 0, y: 10 }}
|
|
32
|
+
animate={{ opacity: 1, y: 0 }}
|
|
33
|
+
onSubmit={handleSubmit}
|
|
34
|
+
className="space-y-4"
|
|
35
|
+
>
|
|
36
|
+
<div className="space-y-2">
|
|
37
|
+
<Label htmlFor="email" className="text-secondary-foreground text-sm">Email</Label>
|
|
38
|
+
<div className="relative">
|
|
39
|
+
<Mail className="absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-muted-foreground" />
|
|
40
|
+
<Input
|
|
41
|
+
id="email"
|
|
42
|
+
type="email"
|
|
43
|
+
placeholder="you@example.com"
|
|
44
|
+
className="pl-10 bg-muted/50 border-border/60 focus:border-primary"
|
|
45
|
+
/>
|
|
46
|
+
</div>
|
|
47
|
+
</div>
|
|
48
|
+
|
|
49
|
+
<div className="space-y-2">
|
|
50
|
+
<Label htmlFor="password" className="text-secondary-foreground text-sm">Password</Label>
|
|
51
|
+
<div className="relative">
|
|
52
|
+
<Lock className="absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-muted-foreground" />
|
|
53
|
+
<Input
|
|
54
|
+
id="password"
|
|
55
|
+
type={showPassword ? "text" : "password"}
|
|
56
|
+
placeholder="••••••••"
|
|
57
|
+
className="pl-10 pr-10 bg-muted/50 border-border/60 focus:border-primary"
|
|
58
|
+
/>
|
|
59
|
+
<button
|
|
60
|
+
type="button"
|
|
61
|
+
onClick={() => setShowPassword(!showPassword)}
|
|
62
|
+
className="absolute right-3 top-1/2 -translate-y-1/2 text-muted-foreground hover:text-foreground transition-colors"
|
|
63
|
+
>
|
|
64
|
+
{showPassword ? <EyeOff className="h-4 w-4" /> : <Eye className="h-4 w-4" />}
|
|
65
|
+
</button>
|
|
66
|
+
</div>
|
|
67
|
+
</div>
|
|
68
|
+
|
|
69
|
+
<Button
|
|
70
|
+
type="submit"
|
|
71
|
+
className="w-full bg-primary text-primary-foreground hover:bg-primary/90"
|
|
72
|
+
disabled={loading}
|
|
73
|
+
>
|
|
74
|
+
{loading ? <Loader2 className="h-4 w-4 animate-spin mr-2" /> : null}
|
|
75
|
+
{loading ? "Signing in..." : "Sign In"}
|
|
76
|
+
{!loading && <ArrowRight className="ml-2 h-4 w-4" />}
|
|
77
|
+
</Button>
|
|
78
|
+
|
|
79
|
+
<div className="relative my-6">
|
|
80
|
+
<div className="absolute inset-0 flex items-center">
|
|
81
|
+
<span className="w-full border-t border-border/60" />
|
|
82
|
+
</div>
|
|
83
|
+
<div className="relative flex justify-center text-xs">
|
|
84
|
+
<span className="bg-card px-3 text-muted-foreground">or continue with</span>
|
|
85
|
+
</div>
|
|
86
|
+
</div>
|
|
87
|
+
|
|
88
|
+
<div className="grid grid-cols-2 gap-3">
|
|
89
|
+
<Button variant="outline" className="bg-muted/30 border-border/60 hover:bg-muted/60">
|
|
90
|
+
<svg className="mr-2 h-4 w-4" viewBox="0 0 24 24"><path fill="currentColor" d="M22.56 12.25c0-.78-.07-1.53-.2-2.25H12v4.26h5.92a5.06 5.06 0 0 1-2.2 3.32v2.77h3.57c2.08-1.92 3.28-4.74 3.28-8.1z"/><path fill="currentColor" d="M12 23c2.97 0 5.46-.98 7.28-2.66l-3.57-2.77c-.98.66-2.23 1.06-3.71 1.06-2.86 0-5.29-1.93-6.16-4.53H2.18v2.84C3.99 20.53 7.7 23 12 23z"/><path fill="currentColor" d="M5.84 14.09c-.22-.66-.35-1.36-.35-2.09s.13-1.43.35-2.09V7.07H2.18C1.43 8.55 1 10.22 1 12s.43 3.45 1.18 4.93l2.85-2.22.81-.62z"/><path fill="currentColor" d="M12 5.38c1.62 0 3.06.56 4.21 1.64l3.15-3.15C17.45 2.09 14.97 1 12 1 7.7 1 3.99 3.47 2.18 7.07l3.66 2.84c.87-2.6 3.3-4.53 6.16-4.53z"/></svg>
|
|
91
|
+
Google
|
|
92
|
+
</Button>
|
|
93
|
+
<Button variant="outline" className="bg-muted/30 border-border/60 hover:bg-muted/60">
|
|
94
|
+
<Github className="mr-2 h-4 w-4" />
|
|
95
|
+
GitHub
|
|
96
|
+
</Button>
|
|
97
|
+
</div>
|
|
98
|
+
</motion.form>
|
|
99
|
+
</TabsContent>
|
|
100
|
+
|
|
101
|
+
<TabsContent value="signup">
|
|
102
|
+
<motion.form
|
|
103
|
+
initial={{ opacity: 0, y: 10 }}
|
|
104
|
+
animate={{ opacity: 1, y: 0 }}
|
|
105
|
+
onSubmit={handleSubmit}
|
|
106
|
+
className="space-y-4"
|
|
107
|
+
>
|
|
108
|
+
<div className="space-y-2">
|
|
109
|
+
<Label className="text-secondary-foreground text-sm">Full Name</Label>
|
|
110
|
+
<div className="relative">
|
|
111
|
+
<User className="absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-muted-foreground" />
|
|
112
|
+
<Input placeholder="John Doe" className="pl-10 bg-muted/50 border-border/60" />
|
|
113
|
+
</div>
|
|
114
|
+
</div>
|
|
115
|
+
<div className="space-y-2">
|
|
116
|
+
<Label className="text-secondary-foreground text-sm">Email</Label>
|
|
117
|
+
<div className="relative">
|
|
118
|
+
<Mail className="absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-muted-foreground" />
|
|
119
|
+
<Input type="email" placeholder="you@example.com" className="pl-10 bg-muted/50 border-border/60" />
|
|
120
|
+
</div>
|
|
121
|
+
</div>
|
|
122
|
+
<div className="space-y-2">
|
|
123
|
+
<Label className="text-secondary-foreground text-sm">Password</Label>
|
|
124
|
+
<div className="relative">
|
|
125
|
+
<Lock className="absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-muted-foreground" />
|
|
126
|
+
<Input type="password" placeholder="Min 8 characters" className="pl-10 bg-muted/50 border-border/60" />
|
|
127
|
+
</div>
|
|
128
|
+
</div>
|
|
129
|
+
<Button type="submit" className="w-full bg-primary text-primary-foreground hover:bg-primary/90" disabled={loading}>
|
|
130
|
+
{loading ? <Loader2 className="h-4 w-4 animate-spin mr-2" /> : null}
|
|
131
|
+
{loading ? "Creating..." : "Create Account"}
|
|
132
|
+
</Button>
|
|
133
|
+
</motion.form>
|
|
134
|
+
</TabsContent>
|
|
135
|
+
|
|
136
|
+
<TabsContent value="forgot">
|
|
137
|
+
<motion.form
|
|
138
|
+
initial={{ opacity: 0, y: 10 }}
|
|
139
|
+
animate={{ opacity: 1, y: 0 }}
|
|
140
|
+
onSubmit={handleSubmit}
|
|
141
|
+
className="space-y-4"
|
|
142
|
+
>
|
|
143
|
+
<p className="text-sm text-muted-foreground mb-4">
|
|
144
|
+
Enter your email and we'll send you a reset link.
|
|
145
|
+
</p>
|
|
146
|
+
<div className="space-y-2">
|
|
147
|
+
<Label className="text-secondary-foreground text-sm">Email</Label>
|
|
148
|
+
<div className="relative">
|
|
149
|
+
<KeyRound className="absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-muted-foreground" />
|
|
150
|
+
<Input type="email" placeholder="you@example.com" className="pl-10 bg-muted/50 border-border/60" />
|
|
151
|
+
</div>
|
|
152
|
+
</div>
|
|
153
|
+
<Button type="submit" className="w-full bg-primary text-primary-foreground hover:bg-primary/90" disabled={loading}>
|
|
154
|
+
{loading ? <Loader2 className="h-4 w-4 animate-spin mr-2" /> : null}
|
|
155
|
+
{loading ? "Sending..." : "Send Reset Link"}
|
|
156
|
+
</Button>
|
|
157
|
+
</motion.form>
|
|
158
|
+
</TabsContent>
|
|
159
|
+
</Tabs>
|
|
160
|
+
</div>
|
|
161
|
+
);
|
|
162
|
+
};
|
|
163
|
+
|
|
164
|
+
export default AuthShowcase;
|