Revert "Unify workspace dropdowns; Update layouts (#458)"

This reverts commit 4bc8448a1d.
This commit is contained in:
gbuomprisco
2026-03-11 14:47:47 +08:00
parent 4bc8448a1d
commit 4912e402a3
530 changed files with 11182 additions and 14382 deletions

View File

@@ -120,7 +120,9 @@ export function AlertDialogStory() {
const generateCode = () => {
let code = `<AlertDialog>\n`;
code += ` <AlertDialogTrigger render={<Button variant="${controls.triggerVariant}">${controls.triggerText}</Button>} />\n`;
code += ` <AlertDialogTrigger asChild>\n`;
code += ` <Button variant="${controls.triggerVariant}">${controls.triggerText}</Button>\n`;
code += ` </AlertDialogTrigger>\n`;
code += ` <AlertDialogContent>\n`;
code += ` <AlertDialogHeader>\n`;
@@ -177,14 +179,11 @@ export function AlertDialogStory() {
const renderPreview = () => {
return (
<AlertDialog>
<AlertDialogTrigger
render={
<Button variant={controls.triggerVariant}>
{controls.triggerText}
</Button>
}
/>
<AlertDialogTrigger asChild>
<Button variant={controls.triggerVariant}>
{controls.triggerText}
</Button>
</AlertDialogTrigger>
<AlertDialogContent>
<AlertDialogHeader>
{controls.withIcon ? (
@@ -342,11 +341,11 @@ export function AlertDialogStory() {
<CardContent className="space-y-4">
<div className="flex flex-wrap gap-3">
<AlertDialog>
<AlertDialogTrigger
render={<Button variant="destructive" size="sm" />}
>
<Trash2 className="mr-2 h-4 w-4" />
Delete Item
<AlertDialogTrigger asChild>
<Button variant="destructive" size="sm">
<Trash2 className="mr-2 h-4 w-4" />
Delete Item
</Button>
</AlertDialogTrigger>
<AlertDialogContent>
<AlertDialogHeader>
@@ -371,9 +370,11 @@ export function AlertDialogStory() {
</AlertDialog>
<AlertDialog>
<AlertDialogTrigger render={<Button variant="outline" />}>
<LogOut className="mr-2 h-4 w-4" />
Sign Out
<AlertDialogTrigger asChild>
<Button variant="outline">
<LogOut className="mr-2 h-4 w-4" />
Sign Out
</Button>
</AlertDialogTrigger>
<AlertDialogContent>
<AlertDialogHeader>
@@ -396,9 +397,11 @@ export function AlertDialogStory() {
</AlertDialog>
<AlertDialog>
<AlertDialogTrigger render={<Button variant="outline" />}>
<UserX className="mr-2 h-4 w-4" />
Remove User
<AlertDialogTrigger asChild>
<Button variant="outline">
<UserX className="mr-2 h-4 w-4" />
Remove User
</Button>
</AlertDialogTrigger>
<AlertDialogContent>
<AlertDialogHeader>
@@ -435,9 +438,11 @@ export function AlertDialogStory() {
<CardContent className="space-y-4">
<div className="flex flex-wrap gap-3">
<AlertDialog>
<AlertDialogTrigger render={<Button variant="outline" />}>
<Archive className="mr-2 h-4 w-4" />
Archive Project
<AlertDialogTrigger asChild>
<Button variant="outline">
<Archive className="mr-2 h-4 w-4" />
Archive Project
</Button>
</AlertDialogTrigger>
<AlertDialogContent>
<AlertDialogHeader>
@@ -460,9 +465,11 @@ export function AlertDialogStory() {
</AlertDialog>
<AlertDialog>
<AlertDialogTrigger render={<Button />}>
<Download className="mr-2 h-4 w-4" />
Export Data
<AlertDialogTrigger asChild>
<Button>
<Download className="mr-2 h-4 w-4" />
Export Data
</Button>
</AlertDialogTrigger>
<AlertDialogContent>
<AlertDialogHeader>
@@ -486,9 +493,11 @@ export function AlertDialogStory() {
</AlertDialog>
<AlertDialog>
<AlertDialogTrigger render={<Button variant="outline" />}>
<RefreshCw className="mr-2 h-4 w-4" />
Reset Settings
<AlertDialogTrigger asChild>
<Button variant="outline">
<RefreshCw className="mr-2 h-4 w-4" />
Reset Settings
</Button>
</AlertDialogTrigger>
<AlertDialogContent>
<AlertDialogHeader>
@@ -526,11 +535,11 @@ export function AlertDialogStory() {
<div className="space-y-3">
<h4 className="text-sm font-semibold">Error/Destructive</h4>
<AlertDialog>
<AlertDialogTrigger
render={<Button variant="destructive" size="sm" />}
>
<Trash2 className="mr-2 h-4 w-4" />
Delete Forever
<AlertDialogTrigger asChild>
<Button variant="destructive" size="sm">
<Trash2 className="mr-2 h-4 w-4" />
Delete Forever
</Button>
</AlertDialogTrigger>
<AlertDialogContent>
<AlertDialogHeader>
@@ -558,11 +567,11 @@ export function AlertDialogStory() {
<div className="space-y-3">
<h4 className="text-sm font-semibold">Warning</h4>
<AlertDialog>
<AlertDialogTrigger
render={<Button variant="outline" size="sm" />}
>
<AlertTriangle className="mr-2 h-4 w-4" />
Unsaved Changes
<AlertDialogTrigger asChild>
<Button variant="outline" size="sm">
<AlertTriangle className="mr-2 h-4 w-4" />
Unsaved Changes
</Button>
</AlertDialogTrigger>
<AlertDialogContent>
<AlertDialogHeader>
@@ -588,11 +597,11 @@ export function AlertDialogStory() {
<div className="space-y-3">
<h4 className="text-sm font-semibold">Info</h4>
<AlertDialog>
<AlertDialogTrigger
render={<Button variant="outline" size="sm" />}
>
<Share className="mr-2 h-4 w-4" />
Share Publicly
<AlertDialogTrigger asChild>
<Button variant="outline" size="sm">
<Share className="mr-2 h-4 w-4" />
Share Publicly
</Button>
</AlertDialogTrigger>
<AlertDialogContent>
<AlertDialogHeader>
@@ -618,9 +627,11 @@ export function AlertDialogStory() {
<div className="space-y-3">
<h4 className="text-sm font-semibold">Success</h4>
<AlertDialog>
<AlertDialogTrigger render={<Button size="sm" />}>
<Download className="mr-2 h-4 w-4" />
Complete Setup
<AlertDialogTrigger asChild>
<Button size="sm">
<Download className="mr-2 h-4 w-4" />
Complete Setup
</Button>
</AlertDialogTrigger>
<AlertDialogContent>
<AlertDialogHeader>

View File

@@ -33,6 +33,7 @@ interface ButtonControls {
loading: boolean;
withIcon: boolean;
fullWidth: boolean;
asChild: boolean;
}
const variantOptions = [
@@ -67,6 +68,7 @@ export function ButtonStory() {
loading: false,
withIcon: false,
fullWidth: false,
asChild: false,
});
const generateCode = () => {
@@ -75,12 +77,14 @@ export function ButtonStory() {
variant: controls.variant,
size: controls.size,
disabled: controls.disabled,
asChild: controls.asChild,
className: controls.fullWidth ? 'w-full' : '',
},
{
variant: 'default',
size: 'default',
disabled: false,
asChild: false,
className: '',
},
);
@@ -190,6 +194,15 @@ export function ButtonStory() {
onCheckedChange={(checked) => updateControl('fullWidth', checked)}
/>
</div>
<div className="flex items-center justify-between">
<Label htmlFor="asChild">As Child</Label>
<Switch
id="asChild"
checked={controls.asChild}
onCheckedChange={(checked) => updateControl('asChild', checked)}
/>
</div>
</>
);

View File

@@ -276,11 +276,11 @@ export default function CalendarStory() {
<Card>
<CardContent className="flex justify-center pt-6">
<Popover>
<PopoverTrigger
render={<Button variant="outline" className="justify-start" />}
>
<CalendarIcon className="mr-2 h-4 w-4" />
Pick a date
<PopoverTrigger asChild>
<Button variant="outline" className="justify-start">
<CalendarIcon className="mr-2 h-4 w-4" />
Pick a date
</Button>
</PopoverTrigger>
<PopoverContent className="w-auto p-0" align="start">
<Calendar

View File

@@ -320,12 +320,10 @@ export function CardButtonStory() {
</thead>
<tbody>
<tr className="border-b">
<td className="p-3 font-mono text-sm">render</td>
<td className="p-3 font-mono text-sm">
React.ReactElement
</td>
<td className="p-3 font-mono text-sm">-</td>
<td className="p-3">Compose with a custom element</td>
<td className="p-3 font-mono text-sm">asChild</td>
<td className="p-3 font-mono text-sm">boolean</td>
<td className="p-3 font-mono text-sm">false</td>
<td className="p-3">Render as child element</td>
</tr>
<tr className="border-b">
<td className="p-3 font-mono text-sm">className</td>

View File

@@ -139,8 +139,8 @@ export function DialogStory() {
});
let code = `<Dialog>\n`;
code += ` <DialogTrigger render={<Button variant="${controls.triggerVariant}" />}>\n`;
code += ` ${controls.triggerText}\n`;
code += ` <DialogTrigger asChild>\n`;
code += ` <Button variant="${controls.triggerVariant}">${controls.triggerText}</Button>\n`;
code += ` </DialogTrigger>\n`;
code += ` <DialogContent${contentPropsString}>\n`;
code += ` <DialogHeader>\n`;
@@ -182,8 +182,8 @@ export function DialogStory() {
if (controls.withFooter) {
code += ` <DialogFooter>\n`;
code += ` <DialogClose render={<Button variant="outline" />}>\n`;
code += ` Cancel\n`;
code += ` <DialogClose asChild>\n`;
code += ` <Button variant="outline">Cancel</Button>\n`;
code += ` </DialogClose>\n`;
code += ` <Button>Save Changes</Button>\n`;
code += ` </DialogFooter>\n`;
@@ -198,8 +198,10 @@ export function DialogStory() {
const renderPreview = () => {
return (
<Dialog>
<DialogTrigger render={<Button variant={controls.triggerVariant} />}>
{controls.triggerText}
<DialogTrigger asChild>
<Button variant={controls.triggerVariant}>
{controls.triggerText}
</Button>
</DialogTrigger>
<DialogContent
className={cn(
@@ -269,8 +271,8 @@ export function DialogStory() {
{controls.withFooter && (
<DialogFooter>
<DialogClose render={<Button variant="outline" />}>
Cancel
<DialogClose asChild>
<Button variant="outline">Cancel</Button>
</DialogClose>
<Button>Save Changes</Button>
</DialogFooter>
@@ -389,9 +391,11 @@ export function DialogStory() {
<CardContent className="space-y-4">
<div className="flex flex-wrap gap-3">
<Dialog>
<DialogTrigger render={<Button variant="outline" />}>
<Info className="mr-2 h-4 w-4" />
Info Dialog
<DialogTrigger asChild>
<Button variant="outline">
<Info className="mr-2 h-4 w-4" />
Info Dialog
</Button>
</DialogTrigger>
<DialogContent>
<DialogHeader>
@@ -408,15 +412,19 @@ export function DialogStory() {
</p>
</div>
<DialogFooter>
<DialogClose render={<Button />}>Got it</DialogClose>
<DialogClose asChild>
<Button>Got it</Button>
</DialogClose>
</DialogFooter>
</DialogContent>
</Dialog>
<Dialog>
<DialogTrigger render={<Button />}>
<Edit className="mr-2 h-4 w-4" />
Edit Profile
<DialogTrigger asChild>
<Button>
<Edit className="mr-2 h-4 w-4" />
Edit Profile
</Button>
</DialogTrigger>
<DialogContent>
<DialogHeader>
@@ -448,8 +456,8 @@ export function DialogStory() {
</div>
</div>
<DialogFooter>
<DialogClose render={<Button variant="outline" />}>
Cancel
<DialogClose asChild>
<Button variant="outline">Cancel</Button>
</DialogClose>
<Button>Save Changes</Button>
</DialogFooter>
@@ -457,9 +465,11 @@ export function DialogStory() {
</Dialog>
<Dialog>
<DialogTrigger render={<Button variant="secondary" />}>
<Settings className="mr-2 h-4 w-4" />
Settings
<DialogTrigger asChild>
<Button variant="secondary">
<Settings className="mr-2 h-4 w-4" />
Settings
</Button>
</DialogTrigger>
<DialogContent>
<DialogHeader>
@@ -489,8 +499,8 @@ export function DialogStory() {
</div>
</div>
<DialogFooter>
<DialogClose render={<Button variant="outline" />}>
Cancel
<DialogClose asChild>
<Button variant="outline">Cancel</Button>
</DialogClose>
<Button>Save</Button>
</DialogFooter>
@@ -508,8 +518,10 @@ export function DialogStory() {
<CardContent className="space-y-4">
<div className="flex flex-wrap gap-3">
<Dialog>
<DialogTrigger render={<Button variant="outline" size="sm" />}>
Small Dialog
<DialogTrigger asChild>
<Button variant="outline" size="sm">
Small Dialog
</Button>
</DialogTrigger>
<DialogContent className="max-w-md">
<DialogHeader>
@@ -524,14 +536,16 @@ export function DialogStory() {
</p>
</div>
<DialogFooter>
<DialogClose render={<Button />}>Close</DialogClose>
<DialogClose asChild>
<Button>Close</Button>
</DialogClose>
</DialogFooter>
</DialogContent>
</Dialog>
<Dialog>
<DialogTrigger render={<Button variant="outline" />}>
Large Dialog
<DialogTrigger asChild>
<Button variant="outline">Large Dialog</Button>
</DialogTrigger>
<DialogContent className="max-w-2xl">
<DialogHeader>
@@ -557,8 +571,8 @@ export function DialogStory() {
</div>
</div>
<DialogFooter>
<DialogClose render={<Button variant="outline" />}>
Cancel
<DialogClose asChild>
<Button variant="outline">Cancel</Button>
</DialogClose>
<Button>Save</Button>
</DialogFooter>
@@ -576,9 +590,11 @@ export function DialogStory() {
<CardContent className="space-y-4">
<div className="flex flex-wrap gap-3">
<Dialog>
<DialogTrigger render={<Button variant="outline" />}>
<Image className="mr-2 h-4 w-4" />
Image Gallery
<DialogTrigger asChild>
<Button variant="outline">
<Image className="mr-2 h-4 w-4" />
Image Gallery
</Button>
</DialogTrigger>
<DialogContent className="max-w-2xl">
<DialogHeader>
@@ -611,9 +627,11 @@ export function DialogStory() {
</Dialog>
<Dialog>
<DialogTrigger render={<Button variant="outline" />}>
<MessageSquare className="mr-2 h-4 w-4" />
Feedback
<DialogTrigger asChild>
<Button variant="outline">
<MessageSquare className="mr-2 h-4 w-4" />
Feedback
</Button>
</DialogTrigger>
<DialogContent>
<DialogHeader>
@@ -650,8 +668,8 @@ export function DialogStory() {
</div>
</div>
<DialogFooter>
<DialogClose render={<Button variant="outline" />}>
Cancel
<DialogClose asChild>
<Button variant="outline">Cancel</Button>
</DialogClose>
<Button>
<MessageSquare className="mr-2 h-4 w-4" />
@@ -718,8 +736,8 @@ export function DialogStory() {
<div>
<h4 className="mb-3 text-lg font-semibold">DialogTrigger</h4>
<p className="text-muted-foreground mb-3 text-sm">
The element that opens the dialog. Use the render prop to compose
with a custom element.
The element that opens the dialog. Use asChild prop to render as
child element.
</p>
</div>

View File

@@ -2,7 +2,7 @@
import { useEffect, useRef, useState } from 'react';
import { useRouter, useSearchParams } from 'next/navigation';
import { useRouter } from 'next/navigation';
import { Code2, FileText, Search } from 'lucide-react';
@@ -35,7 +35,6 @@ export function DocsSidebar({
selectedCategory,
}: DocsSidebarProps) {
const [searchQuery, setSearchQuery] = useState('');
const searchParams = useSearchParams();
const router = useRouter();
const filteredComponents = COMPONENTS_REGISTRY.filter((c) =>
@@ -51,21 +50,21 @@ export function DocsSidebar({
.sort((a, b) => a.name.localeCompare(b.name));
const onCategorySelect = (category: string | null) => {
const sp = new URLSearchParams(searchParams);
sp.set('category', category || '');
router.push(`/components?${sp.toString()}`);
const searchParams = new URLSearchParams(window.location.search);
searchParams.set('category', category || '');
router.push(`/components?${searchParams.toString()}`);
};
const onComponentSelect = (component: ComponentInfo) => {
const sp = new URLSearchParams(searchParams);
sp.set('component', component.name);
router.push(`/components?${sp.toString()}`);
const searchParams = new URLSearchParams(window.location.search);
searchParams.set('component', component.name);
router.push(`/components?${searchParams.toString()}`);
};
return (
<div className="bg-muted/30 flex h-screen w-80 flex-col overflow-hidden border-r">
{/* Header */}
<div className="shrink-0 border-b p-4">
<div className="flex-shrink-0 border-b p-4">
<div className="mb-2 flex items-center gap-2">
<Code2 className="text-primary h-6 w-6" />
@@ -78,14 +77,13 @@ export function DocsSidebar({
</div>
{/* Controls */}
<div className="shrink-0 space-y-2 border-b p-4">
<div className="flex-shrink-0 space-y-2 border-b p-4">
{/* Category Select */}
<div className="space-y-2">
<Select
defaultValue={selectedCategory || 'all'}
value={selectedCategory || 'all'}
onValueChange={(value) => {
const category = value === 'all' ? null : value;
onCategorySelect(category);
// Select first component in the filtered results
@@ -98,12 +96,8 @@ export function DocsSidebar({
}
}}
>
<SelectTrigger className="w-full">
<SelectValue>
{(category) => {
return category === 'all' ? 'All Categories' : category;
}}
</SelectValue>
<SelectTrigger>
<SelectValue placeholder={'Select a category'} />
</SelectTrigger>
<SelectContent>
@@ -160,7 +154,7 @@ export function DocsSidebar({
{/* Components List - Scrollable */}
<div className="flex flex-1 flex-col overflow-y-auto">
<div className="shrink-0 p-4 pb-2">
<div className="flex-shrink-0 p-4 pb-2">
<h3 className="flex items-center gap-2 text-sm font-semibold">
<FileText className="h-4 w-4" />
Components

View File

@@ -101,18 +101,13 @@ const examples = [
return (
<div className="flex min-h-32 items-center justify-center">
<DropdownMenu>
<DropdownMenuTrigger
render={
<Button
variant="ghost"
className="relative h-8 w-8 rounded-full"
/>
}
>
<Avatar className="h-8 w-8">
<AvatarImage src="/avatars/01.png" alt="@username" />
<AvatarFallback>JD</AvatarFallback>
</Avatar>
<DropdownMenuTrigger asChild>
<Button variant="ghost" className="relative h-8 w-8 rounded-full">
<Avatar className="h-8 w-8">
<AvatarImage src="/avatars/01.png" alt="@username" />
<AvatarFallback>JD</AvatarFallback>
</Avatar>
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent className="w-56" align="end" forceMount>
<DropdownMenuLabel className="font-normal">
@@ -190,11 +185,11 @@ const examples = [
</div>
<DropdownMenu>
<DropdownMenuTrigger
render={<Button variant="ghost" className="h-8 w-8 p-0" />}
>
<span className="sr-only">Open menu</span>
<MoreHorizontal className="h-4 w-4" />
<DropdownMenuTrigger asChild>
<Button variant="ghost" className="h-8 w-8 p-0">
<span className="sr-only">Open menu</span>
<MoreHorizontal className="h-4 w-4" />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end" className="w-48">
<DropdownMenuItem onClick={() => setSelectedAction('open')}>
@@ -280,9 +275,11 @@ const examples = [
return (
<div className="flex min-h-48 items-center justify-center">
<DropdownMenu>
<DropdownMenuTrigger render={<Button variant="outline" />}>
<Plus className="mr-2 h-4 w-4" />
Create New
<DropdownMenuTrigger asChild>
<Button variant="outline">
<Plus className="mr-2 h-4 w-4" />
Create New
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent className="w-56">
<DropdownMenuLabel>Create Content</DropdownMenuLabel>
@@ -396,11 +393,11 @@ const examples = [
<span className="text-sm">Appearance & Layout</span>
<DropdownMenu>
<DropdownMenuTrigger
render={<Button variant="outline" size="sm" />}
>
<Settings className="mr-2 h-4 w-4" />
Configure
<DropdownMenuTrigger asChild>
<Button variant="outline" size="sm">
<Settings className="mr-2 h-4 w-4" />
Configure
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent className="w-64" align="end">
<DropdownMenuLabel>View Options</DropdownMenuLabel>
@@ -550,10 +547,10 @@ const examples = [
</div>
<DropdownMenu>
<DropdownMenuTrigger
render={<Button variant="ghost" className="h-8 w-8 p-0" />}
>
<MoreHorizontal className="h-4 w-4" />
<DropdownMenuTrigger asChild>
<Button variant="ghost" className="h-8 w-8 p-0">
<MoreHorizontal className="h-4 w-4" />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end" className="w-48">
<DropdownMenuItem
@@ -866,7 +863,7 @@ export default function DropdownMenuStory() {
modal: controls.modal ? true : undefined,
});
const dropdownStructure = `<DropdownMenu${rootProps}>\n <DropdownMenuTrigger render={<Button variant="outline" />}>\n Open Menu\n </DropdownMenuTrigger>\n <DropdownMenuContent${contentProps}>\n <DropdownMenuItem>\n <User className="mr-2 h-4 w-4" />\n <span>Profile</span>\n </DropdownMenuItem>\n <DropdownMenuItem>\n <Settings className="mr-2 h-4 w-4" />\n <span>Settings</span>\n </DropdownMenuItem>\n <DropdownMenuSeparator />\n <DropdownMenuItem>\n <LogOut className="mr-2 h-4 w-4" />\n <span>Log out</span>\n </DropdownMenuItem>\n </DropdownMenuContent>\n</DropdownMenu>`;
const dropdownStructure = `<DropdownMenu${rootProps}>\n <DropdownMenuTrigger asChild>\n <Button variant="outline">Open Menu</Button>\n </DropdownMenuTrigger>\n <DropdownMenuContent${contentProps}>\n <DropdownMenuItem>\n <User className="mr-2 h-4 w-4" />\n <span>Profile</span>\n </DropdownMenuItem>\n <DropdownMenuItem>\n <Settings className="mr-2 h-4 w-4" />\n <span>Settings</span>\n </DropdownMenuItem>\n <DropdownMenuSeparator />\n <DropdownMenuItem>\n <LogOut className="mr-2 h-4 w-4" />\n <span>Log out</span>\n </DropdownMenuItem>\n </DropdownMenuContent>\n</DropdownMenu>`;
return `${importStatement}\n${buttonImport}\n${iconImport}\n\n${dropdownStructure}`;
};
@@ -974,8 +971,8 @@ export default function DropdownMenuStory() {
const previewContent = (
<div className="flex justify-center p-6">
<DropdownMenu modal={controls.modal}>
<DropdownMenuTrigger render={<Button variant="outline" />}>
Open Menu
<DropdownMenuTrigger asChild>
<Button variant="outline">Open Menu</Button>
</DropdownMenuTrigger>
<DropdownMenuContent
side={controls.side}

View File

@@ -4,7 +4,7 @@ import { useState } from 'react';
import { zodResolver } from '@hookform/resolvers/zod';
import { useForm } from 'react-hook-form';
import * as z from 'zod';
import { z } from 'zod';
import { Badge } from '@kit/ui/badge';
import { Button } from '@kit/ui/button';
@@ -119,7 +119,7 @@ export default function FormStory() {
const formImport = generateImportStatement(formComponents, '@kit/ui/form');
const inputImport = generateImportStatement(['Input'], '@kit/ui/input');
const buttonImport = generateImportStatement(['Button'], '@kit/ui/button');
const hookFormImports = `import { useForm } from 'react-hook-form';\nimport { zodResolver } from '@hookform/resolvers/zod';\nimport * as z from 'zod';`;
const hookFormImports = `import { useForm } from 'react-hook-form';\nimport { zodResolver } from '@hookform/resolvers/zod';\nimport { z } from 'zod';`;
let schemaCode = '';
let formFieldsCode = '';
@@ -130,19 +130,19 @@ export default function FormStory() {
formFieldsCode = ` <FormField\n control={form.control}\n name="username"\n render={({ field }) => (\n <FormItem>\n <FormLabel>Username</FormLabel>\n <FormControl>\n <Input ${controls.disabled ? 'disabled ' : ''}placeholder="Enter username" {...field} />\n </FormControl>${controls.showDescriptions ? '\n <FormDescription>\n Your public display name.\n </FormDescription>' : ''}${controls.showValidation ? '\n <FormMessage />' : ''}\n </FormItem>\n )}\n />\n <FormField\n control={form.control}\n name="email"\n render={({ field }) => (\n <FormItem>\n <FormLabel>Email</FormLabel>\n <FormControl>\n <Input ${controls.disabled ? 'disabled ' : ''}type="email" placeholder="Enter email" {...field} />\n </FormControl>${controls.showDescriptions ? "\n <FormDescription>\n We'll never share your email.\n </FormDescription>" : ''}${controls.showValidation ? '\n <FormMessage />' : ''}\n </FormItem>\n )}\n />`;
onSubmitCode = ` function onSubmit(values: z.output<typeof formSchema>) {\n console.log('Form submitted:', values);\n }`;
onSubmitCode = ` function onSubmit(values: z.infer<typeof formSchema>) {\n console.log('Form submitted:', values);\n }`;
} else if (controls.formType === 'advanced') {
schemaCode = `const formSchema = z.object({\n firstName: z.string().min(1, 'First name is required.'),\n lastName: z.string().min(1, 'Last name is required.'),\n email: z.string().email('Please enter a valid email address.'),\n});`;
formFieldsCode = ` <FormField\n control={form.control}\n name="firstName"\n render={({ field }) => (\n <FormItem>\n <FormLabel>First Name</FormLabel>\n <FormControl>\n <Input ${controls.disabled ? 'disabled ' : ''}placeholder="John" {...field} />\n </FormControl>${controls.showValidation ? '\n <FormMessage />' : ''}\n </FormItem>\n )}\n />\n <FormField\n control={form.control}\n name="lastName"\n render={({ field }) => (\n <FormItem>\n <FormLabel>Last Name</FormLabel>\n <FormControl>\n <Input ${controls.disabled ? 'disabled ' : ''}placeholder="Doe" {...field} />\n </FormControl>${controls.showValidation ? '\n <FormMessage />' : ''}\n </FormItem>\n )}\n />`;
onSubmitCode = ` function onSubmit(values: z.output<typeof formSchema>) {\n console.log('Advanced form submitted:', values);\n }`;
onSubmitCode = ` function onSubmit(values: z.infer<typeof formSchema>) {\n console.log('Advanced form submitted:', values);\n }`;
} else {
schemaCode = `const formSchema = z.object({\n password: z.string().min(8, 'Password must be at least 8 characters.'),\n confirmPassword: z.string(),\n}).refine((data) => data.password === data.confirmPassword, {\n message: 'Passwords do not match.',\n path: ['confirmPassword'],\n});`;
formFieldsCode = ` <FormField\n control={form.control}\n name="password"\n render={({ field }) => (\n <FormItem>\n <FormLabel>Password</FormLabel>\n <FormControl>\n <Input ${controls.disabled ? 'disabled ' : ''}type="password" {...field} />\n </FormControl>${controls.showValidation ? '\n <FormMessage />' : ''}\n </FormItem>\n )}\n />\n <FormField\n control={form.control}\n name="confirmPassword"\n render={({ field }) => (\n <FormItem>\n <FormLabel>Confirm Password</FormLabel>\n <FormControl>\n <Input ${controls.disabled ? 'disabled ' : ''}type="password" {...field} />\n </FormControl>${controls.showValidation ? '\n <FormMessage />' : ''}\n </FormItem>\n )}\n />`;
onSubmitCode = ` function onSubmit(values: z.output<typeof formSchema>) {\n console.log('Validation form submitted:', values);\n }`;
onSubmitCode = ` function onSubmit(values: z.infer<typeof formSchema>) {\n console.log('Validation form submitted:', values);\n }`;
}
const defaultValuesCode =
@@ -152,13 +152,13 @@ export default function FormStory() {
? ` defaultValues: {\n firstName: '',\n lastName: '',\n email: '',\n },`
: ` defaultValues: {\n password: '',\n confirmPassword: '',\n },`;
const fullFormCode = `${hookFormImports}\n${formImport}\n${inputImport}\n${buttonImport}\n\n${schemaCode}\n\nfunction MyForm() {\n const form = useForm<z.output<typeof formSchema>>({\n resolver: zodResolver(formSchema),\n${defaultValuesCode}\n });\n\n${onSubmitCode}\n\n return (\n <Form {...form}>\n <form onSubmit={form.handleSubmit(onSubmit)} className="space-y-4">\n${formFieldsCode}\n <Button type="submit"${controls.disabled ? ' disabled' : ''}>Submit</Button>\n </form>\n </Form>\n );\n}`;
const fullFormCode = `${hookFormImports}\n${formImport}\n${inputImport}\n${buttonImport}\n\n${schemaCode}\n\nfunction MyForm() {\n const form = useForm<z.infer<typeof formSchema>>({\n resolver: zodResolver(formSchema),\n${defaultValuesCode}\n });\n\n${onSubmitCode}\n\n return (\n <Form {...form}>\n <form onSubmit={form.handleSubmit(onSubmit)} className="space-y-4">\n${formFieldsCode}\n <Button type="submit"${controls.disabled ? ' disabled' : ''}>Submit</Button>\n </form>\n </Form>\n );\n}`;
return fullFormCode;
};
// Basic form
const basicForm = useForm<z.output<typeof basicFormSchema>>({
const basicForm = useForm<z.infer<typeof basicFormSchema>>({
resolver: zodResolver(basicFormSchema),
defaultValues: {
username: '',
@@ -169,7 +169,7 @@ export default function FormStory() {
});
// Advanced form
const advancedForm = useForm<z.output<typeof advancedFormSchema>>({
const advancedForm = useForm<z.infer<typeof advancedFormSchema>>({
resolver: zodResolver(advancedFormSchema),
defaultValues: {
firstName: '',
@@ -183,7 +183,7 @@ export default function FormStory() {
});
// Validation form
const validationForm = useForm<z.output<typeof validationFormSchema>>({
const validationForm = useForm<z.infer<typeof validationFormSchema>>({
resolver: zodResolver(validationFormSchema),
defaultValues: {
password: '',
@@ -1056,7 +1056,7 @@ export default function FormStory() {
<pre className="overflow-x-auto text-sm">
{`import { useForm } from 'react-hook-form';
import { zodResolver } from '@hookform/resolvers/zod';
import * as z from 'zod';
import { z } from 'zod';
import { Form, FormField, FormItem, FormLabel, FormControl, FormMessage } from '@kit/ui/form';
const formSchema = z.object({
@@ -1065,7 +1065,7 @@ const formSchema = z.object({
});
function MyForm() {
const form = useForm<z.output<typeof formSchema>>({
const form = useForm<z.infer<typeof formSchema>>({
resolver: zodResolver(formSchema),
defaultValues: {
username: '',
@@ -1073,7 +1073,7 @@ function MyForm() {
},
});
function onSubmit(values: z.output<typeof formSchema>) {
function onSubmit(values: z.infer<typeof formSchema>) {
console.log(values);
}

View File

@@ -99,7 +99,7 @@ export function KbdStory() {
let snippet = groupLines.join('\n');
if (controls.showTooltip) {
snippet = `<TooltipProvider>\n <Tooltip>\n <TooltipTrigger render={<Button variant="outline" />}>\n Command palette\n </TooltipTrigger>\n <TooltipContent className="flex items-center gap-2">\n <span>Press</span>\n ${groupLines.join('\n ')}\n </TooltipContent>\n </Tooltip>\n</TooltipProvider>`;
snippet = `<TooltipProvider>\n <Tooltip>\n <TooltipTrigger asChild>\n <Button variant="outline">Command palette</Button>\n </TooltipTrigger>\n <TooltipContent className="flex items-center gap-2">\n <span>Press</span>\n ${groupLines.join('\n ')}\n </TooltipContent>\n </Tooltip>\n</TooltipProvider>`;
}
return formatCodeBlock(snippet, [
@@ -115,11 +115,11 @@ export function KbdStory() {
{controls.showTooltip ? (
<TooltipProvider delayDuration={200}>
<Tooltip>
<TooltipTrigger
render={<Button variant="outline" className="gap-2" />}
>
<Command className="h-4 w-4" />
Command palette
<TooltipTrigger asChild>
<Button variant="outline" className="gap-2">
<Command className="h-4 w-4" />
Command palette
</Button>
</TooltipTrigger>
<TooltipContent className="flex items-center gap-2">
<span>Press</span>

View File

@@ -136,13 +136,11 @@ export function SimpleDataTableStory() {
{controls.showActions && (
<TableCell>
<DropdownMenu>
<DropdownMenuTrigger
render={
<Button variant="ghost" className="h-8 w-8 p-0" />
}
>
<span className="sr-only">Open menu</span>
<MoreHorizontal className="h-4 w-4" />
<DropdownMenuTrigger asChild>
<Button variant="ghost" className="h-8 w-8 p-0">
<span className="sr-only">Open menu</span>
<MoreHorizontal className="h-4 w-4" />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end">
<DropdownMenuItem

View File

@@ -100,7 +100,7 @@ export function SwitchStory() {
className: cn(
controls.size === 'sm' && 'h-4 w-7',
controls.size === 'lg' && 'h-6 w-11',
controls.error && 'data-checked:bg-destructive',
controls.error && 'data-[state=checked]:bg-destructive',
),
};
@@ -200,7 +200,7 @@ export function SwitchStory() {
className={cn(
controls.size === 'sm' && 'h-4 w-7',
controls.size === 'lg' && 'h-6 w-11',
controls.error && 'data-checked:bg-destructive',
controls.error && 'data-[state=checked]:bg-destructive',
)}
/>
);
@@ -616,7 +616,7 @@ export function SwitchStory() {
</Label>
<Switch
id="error-switch"
className="data-checked:bg-destructive"
className="data-[state=checked]:bg-destructive"
/>
</div>
<p className="text-destructive text-sm">
@@ -642,7 +642,7 @@ export function SwitchStory() {
<div>
<h4 className="mb-3 text-lg font-semibold">Switch</h4>
<p className="text-muted-foreground mb-3 text-sm">
A toggle switch component for boolean states. Built on Base UI
A toggle switch component for boolean states. Built on Radix UI
Switch primitive.
</p>
<div className="overflow-x-auto">

View File

@@ -62,9 +62,9 @@ interface TabsControlsProps {
const variantClasses = {
default: '',
pills:
'[&>div]:bg-background [&>div]:border [&>div]:rounded-lg [&>div]:p-1 [&_button]:rounded-md [&_button[data-active]]:bg-primary [&_button[data-active]]:text-primary-foreground',
'[&>div]:bg-background [&>div]:border [&>div]:rounded-lg [&>div]:p-1 [&_button]:rounded-md [&_button[data-state=active]]:bg-primary [&_button[data-state=active]]:text-primary-foreground',
underline:
'[&>div]:bg-transparent [&>div]:border-b [&>div]:rounded-none [&_button]:rounded-none [&_button]:border-b-2 [&_button]:border-transparent [&_button[data-active]]:border-primary [&_button[data-active]]:bg-transparent',
'[&>div]:bg-transparent [&>div]:border-b [&>div]:rounded-none [&_button]:rounded-none [&_button]:border-b-2 [&_button]:border-transparent [&_button[data-state=active]]:border-primary [&_button[data-state=active]]:bg-transparent',
};
const sizeClasses = {
@@ -683,28 +683,28 @@ function App() {
<TabsList className="h-auto rounded-none border-b bg-transparent p-0">
<TabsTrigger
value="overview"
className="data-active:border-primary rounded-none border-b-2 border-transparent data-active:bg-transparent"
className="data-[state=active]:border-primary rounded-none border-b-2 border-transparent data-[state=active]:bg-transparent"
>
<BarChart3 className="mr-2 h-4 w-4" />
Overview
</TabsTrigger>
<TabsTrigger
value="users"
className="data-active:border-primary rounded-none border-b-2 border-transparent data-active:bg-transparent"
className="data-[state=active]:border-primary rounded-none border-b-2 border-transparent data-[state=active]:bg-transparent"
>
<User className="mr-2 h-4 w-4" />
Users
</TabsTrigger>
<TabsTrigger
value="revenue"
className="data-active:border-primary rounded-none border-b-2 border-transparent data-active:bg-transparent"
className="data-[state=active]:border-primary rounded-none border-b-2 border-transparent data-[state=active]:bg-transparent"
>
<CreditCard className="mr-2 h-4 w-4" />
Revenue
</TabsTrigger>
<TabsTrigger
value="reports"
className="data-active:border-primary rounded-none border-b-2 border-transparent data-active:bg-transparent"
className="data-[state=active]:border-primary rounded-none border-b-2 border-transparent data-[state=active]:bg-transparent"
>
<FileText className="mr-2 h-4 w-4" />
Reports
@@ -905,7 +905,8 @@ const apiReference = {
{
name: '...props',
type: 'React.ComponentPropsWithoutRef<typeof TabsPrimitive.Root>',
description: 'All additional props from Base UI Tabs.Root component.',
description:
'All props from Radix UI Tabs.Root component including asChild, id, etc.',
},
],
examples: [

View File

@@ -144,23 +144,22 @@ function TooltipStory() {
let code = `<TooltipProvider${providerPropsString}>\n`;
code += ` <Tooltip>\n`;
code += ` <TooltipTrigger asChild>\n`;
if (controls.triggerType === 'button') {
code += ` <TooltipTrigger render={<Button variant="${controls.triggerVariant}" />}>\n`;
code += ` Hover me\n`;
code += ` <Button variant="${controls.triggerVariant}">Hover me</Button>\n`;
} else if (controls.triggerType === 'icon') {
code += ` <Button variant="${controls.triggerVariant}" size="icon">\n`;
const iconName = selectedIconData?.icon.name || 'Info';
code += ` <TooltipTrigger render={<Button variant="${controls.triggerVariant}" size="icon" />}>\n`;
code += ` <${iconName} className="h-4 w-4" />\n`;
code += ` <${iconName} className="h-4 w-4" />\n`;
code += ` </Button>\n`;
} else if (controls.triggerType === 'text') {
code += ` <TooltipTrigger render={<span className="cursor-help underline decoration-dotted" />}>\n`;
code += ` Hover me\n`;
code += ` <span className="cursor-help underline decoration-dotted">Hover me</span>\n`;
} else if (controls.triggerType === 'input') {
code += ` <TooltipTrigger render={<Input placeholder="Hover over this input" />} />\n`;
code += ` <Input placeholder="Hover over this input" />\n`;
}
if (controls.triggerType !== 'input') {
code += ` </TooltipTrigger>\n`;
}
code += ` </TooltipTrigger>\n`;
code += ` <TooltipContent${contentPropsString}>\n`;
code += ` <p>${controls.content}</p>\n`;
code += ` </TooltipContent>\n`;
@@ -171,50 +170,28 @@ function TooltipStory() {
};
const renderPreview = () => {
const renderTrigger = () => {
const trigger = (() => {
switch (controls.triggerType) {
case 'button':
return (
<TooltipTrigger
render={<Button variant={controls.triggerVariant} />}
>
Hover me
</TooltipTrigger>
);
return <Button variant={controls.triggerVariant}>Hover me</Button>;
case 'icon':
return (
<TooltipTrigger
render={<Button variant={controls.triggerVariant} size="icon" />}
>
<Button variant={controls.triggerVariant} size="icon">
<IconComponent className="h-4 w-4" />
</TooltipTrigger>
</Button>
);
case 'text':
return (
<TooltipTrigger
render={
<span className="cursor-help underline decoration-dotted" />
}
>
<span className="cursor-help underline decoration-dotted">
Hover me
</TooltipTrigger>
</span>
);
case 'input':
return (
<TooltipTrigger
render={<Input placeholder="Hover over this input" />}
/>
);
return <Input placeholder="Hover over this input" />;
default:
return (
<TooltipTrigger
render={<Button variant={controls.triggerVariant} />}
>
Hover me
</TooltipTrigger>
);
return <Button variant={controls.triggerVariant}>Hover me</Button>;
}
};
})();
return (
<div className="flex min-h-[200px] items-center justify-center">
@@ -224,7 +201,7 @@ function TooltipStory() {
disableHoverableContent={controls.disableHoverableContent}
>
<Tooltip>
{renderTrigger()}
<TooltipTrigger asChild>{trigger}</TooltipTrigger>
<TooltipContent
side={controls.side}
align={controls.align}
@@ -399,9 +376,11 @@ function TooltipStory() {
<TooltipProvider>
<div className="flex flex-wrap gap-4">
<Tooltip>
<TooltipTrigger render={<Button variant="outline" />}>
<Info className="mr-2 h-4 w-4" />
Info Button
<TooltipTrigger asChild>
<Button variant="outline">
<Info className="mr-2 h-4 w-4" />
Info Button
</Button>
</TooltipTrigger>
<TooltipContent>
<p>This provides additional information</p>
@@ -409,8 +388,10 @@ function TooltipStory() {
</Tooltip>
<Tooltip>
<TooltipTrigger render={<Button variant="ghost" size="icon" />}>
<HelpCircle className="h-4 w-4" />
<TooltipTrigger asChild>
<Button variant="ghost" size="icon">
<HelpCircle className="h-4 w-4" />
</Button>
</TooltipTrigger>
<TooltipContent>
<p>Click for help documentation</p>
@@ -418,12 +399,10 @@ function TooltipStory() {
</Tooltip>
<Tooltip>
<TooltipTrigger
render={
<span className="cursor-help underline decoration-dotted" />
}
>
Hover for explanation
<TooltipTrigger asChild>
<span className="cursor-help underline decoration-dotted">
Hover for explanation
</span>
</TooltipTrigger>
<TooltipContent>
<p>This term needs clarification for better understanding</p>
@@ -431,9 +410,9 @@ function TooltipStory() {
</Tooltip>
<Tooltip>
<TooltipTrigger
render={<Input placeholder="Hover me" className="w-48" />}
/>
<TooltipTrigger asChild>
<Input placeholder="Hover me" className="w-48" />
</TooltipTrigger>
<TooltipContent>
<p>Enter your email address here</p>
</TooltipContent>
@@ -455,10 +434,10 @@ function TooltipStory() {
{/* Top Row */}
<div></div>
<Tooltip>
<TooltipTrigger
render={<Button variant="outline" size="sm" />}
>
Top
<TooltipTrigger asChild>
<Button variant="outline" size="sm">
Top
</Button>
</TooltipTrigger>
<TooltipContent side="top">
<p>Tooltip on top</p>
@@ -468,10 +447,10 @@ function TooltipStory() {
{/* Middle Row */}
<Tooltip>
<TooltipTrigger
render={<Button variant="outline" size="sm" />}
>
Left
<TooltipTrigger asChild>
<Button variant="outline" size="sm">
Left
</Button>
</TooltipTrigger>
<TooltipContent side="left">
<p>Tooltip on left</p>
@@ -481,10 +460,10 @@ function TooltipStory() {
<span className="text-muted-foreground text-sm">Center</span>
</div>
<Tooltip>
<TooltipTrigger
render={<Button variant="outline" size="sm" />}
>
Right
<TooltipTrigger asChild>
<Button variant="outline" size="sm">
Right
</Button>
</TooltipTrigger>
<TooltipContent side="right">
<p>Tooltip on right</p>
@@ -494,10 +473,10 @@ function TooltipStory() {
{/* Bottom Row */}
<div></div>
<Tooltip>
<TooltipTrigger
render={<Button variant="outline" size="sm" />}
>
Bottom
<TooltipTrigger asChild>
<Button variant="outline" size="sm">
Bottom
</Button>
</TooltipTrigger>
<TooltipContent side="bottom">
<p>Tooltip on bottom</p>
@@ -519,9 +498,11 @@ function TooltipStory() {
<TooltipProvider>
<div className="flex flex-wrap gap-4">
<Tooltip>
<TooltipTrigger render={<Button variant="outline" />}>
<Star className="mr-2 h-4 w-4" />
Premium Feature
<TooltipTrigger asChild>
<Button variant="outline">
<Star className="mr-2 h-4 w-4" />
Premium Feature
</Button>
</TooltipTrigger>
<TooltipContent className="max-w-xs">
<div className="space-y-1">
@@ -535,9 +516,11 @@ function TooltipStory() {
</Tooltip>
<Tooltip>
<TooltipTrigger render={<Button variant="outline" />}>
<Settings className="mr-2 h-4 w-4" />
Advanced Settings
<TooltipTrigger asChild>
<Button variant="outline">
<Settings className="mr-2 h-4 w-4" />
Advanced Settings
</Button>
</TooltipTrigger>
<TooltipContent>
<div className="space-y-1">
@@ -554,9 +537,11 @@ function TooltipStory() {
</Tooltip>
<Tooltip>
<TooltipTrigger render={<Button variant="destructive" />}>
<AlertCircle className="mr-2 h-4 w-4" />
Delete Account
<TooltipTrigger asChild>
<Button variant="destructive">
<AlertCircle className="mr-2 h-4 w-4" />
Delete Account
</Button>
</TooltipTrigger>
<TooltipContent className="border-destructive bg-destructive text-destructive-foreground max-w-xs">
<div className="space-y-1">
@@ -583,10 +568,10 @@ function TooltipStory() {
<div className="space-y-4">
<div className="flex items-center gap-4">
<Tooltip>
<TooltipTrigger
render={<Button size="icon" variant="ghost" />}
>
<Copy className="h-4 w-4" />
<TooltipTrigger asChild>
<Button size="icon" variant="ghost">
<Copy className="h-4 w-4" />
</Button>
</TooltipTrigger>
<TooltipContent>
<p>Copy to clipboard</p>
@@ -594,10 +579,10 @@ function TooltipStory() {
</Tooltip>
<Tooltip>
<TooltipTrigger
render={<Button size="icon" variant="ghost" />}
>
<Download className="h-4 w-4" />
<TooltipTrigger asChild>
<Button size="icon" variant="ghost">
<Download className="h-4 w-4" />
</Button>
</TooltipTrigger>
<TooltipContent>
<p>Download file</p>
@@ -605,10 +590,10 @@ function TooltipStory() {
</Tooltip>
<Tooltip>
<TooltipTrigger
render={<Button size="icon" variant="ghost" />}
>
<Share className="h-4 w-4" />
<TooltipTrigger asChild>
<Button size="icon" variant="ghost">
<Share className="h-4 w-4" />
</Button>
</TooltipTrigger>
<TooltipContent>
<p>Share with others</p>
@@ -620,11 +605,9 @@ function TooltipStory() {
<div className="space-y-2">
<Label htmlFor="username">Username</Label>
<Tooltip>
<TooltipTrigger
render={
<Input id="username" placeholder="Enter username" />
}
/>
<TooltipTrigger asChild>
<Input id="username" placeholder="Enter username" />
</TooltipTrigger>
<TooltipContent>
<p>Must be 3-20 characters, letters and numbers only</p>
</TooltipContent>
@@ -633,7 +616,9 @@ function TooltipStory() {
<div className="flex items-center space-x-2">
<Tooltip>
<TooltipTrigger render={<Checkbox id="terms" />} />
<TooltipTrigger asChild>
<Checkbox id="terms" />
</TooltipTrigger>
<TooltipContent className="max-w-xs">
<p>
By checking this, you agree to our Terms of Service and
@@ -766,7 +751,7 @@ function TooltipStory() {
</li>
<li>
<strong>TooltipTrigger:</strong> Element that triggers the
tooltip (use render prop)
tooltip (use asChild prop)
</li>
</ul>
</div>

View File

@@ -492,7 +492,7 @@ export const COMPONENTS_REGISTRY: ComponentInfo[] = [
status: 'stable',
component: CardButtonStory,
sourceFile: '@kit/ui/card-button',
props: ['className', 'children', 'onClick', 'disabled'],
props: ['asChild', 'className', 'children', 'onClick', 'disabled'],
icon: MousePointer,
},
@@ -950,7 +950,7 @@ export const COMPONENTS_REGISTRY: ComponentInfo[] = [
status: 'stable',
component: ItemStory,
sourceFile: '@kit/ui/item',
props: ['variant', 'size', 'className'],
props: ['variant', 'size', 'asChild', 'className'],
icon: Layers,
},
@@ -1004,7 +1004,7 @@ export const COMPONENTS_REGISTRY: ComponentInfo[] = [
status: 'stable',
component: BreadcrumbStory,
sourceFile: '@kit/ui/breadcrumb',
props: ['separator', 'href', 'className'],
props: ['separator', 'asChild', 'href', 'className'],
icon: ChevronRight,
},

View File

@@ -1,3 +1,4 @@
import { withI18n } from '../../lib/i18n/with-i18n';
import { DocsContent } from './components/docs-content';
import { DocsHeader } from './components/docs-header';
import { DocsSidebar } from './components/docs-sidebar';
@@ -28,4 +29,4 @@ async function ComponentDocsPage(props: ComponentDocsPageProps) {
);
}
export default ComponentDocsPage;
export default withI18n(ComponentDocsPage);

View File

@@ -67,10 +67,10 @@ export default async function EmailPage(props: EmailPageProps) {
Remember that the below is an approximation of the email. Always test
it in your inbox.{' '}
<Dialog>
<DialogTrigger
render={<Button variant={'link'} className="p-0 underline" />}
>
Test Email
<DialogTrigger asChild>
<Button variant={'link'} className="p-0 underline">
Test Email
</Button>
</DialogTrigger>
<DialogContent>

View File

@@ -1,4 +1,4 @@
import * as z from 'zod';
import { z } from 'zod';
export const EmailTesterFormSchema = z.object({
username: z.string().min(1),

View File

@@ -49,16 +49,13 @@ export default async function EmailsPage() {
<div className={'grid grid-cols-1 gap-4 md:grid-cols-4'}>
{categoryTemplates.map((template) => (
<CardButton
key={template.id}
render={
<Link href={`/emails/${template.id}`}>
<CardButtonHeader>
<CardButtonTitle>{template.name}</CardButtonTitle>
</CardButtonHeader>
</Link>
}
/>
<CardButton key={template.id} asChild>
<Link href={`/emails/${template.id}`}>
<CardButtonHeader>
<CardButtonTitle>{template.name}</CardButtonTitle>
</CardButtonHeader>
</Link>
</CardButton>
))}
</div>
</div>

View File

@@ -2,7 +2,6 @@ import type { Metadata } from 'next';
import { DevToolLayout } from '@/components/app-layout';
import { RootProviders } from '@/components/root-providers';
import { getMessages } from 'next-intl/server';
import '../styles/globals.css';
@@ -11,17 +10,15 @@ export const metadata: Metadata = {
description: 'The dev tool for Makerkit',
};
export default async function RootLayout({
export default function RootLayout({
children,
}: Readonly<{
children: React.ReactNode;
}>) {
const messages = await getMessages();
return (
<html lang="en">
<body>
<RootProviders messages={messages}>
<RootProviders>
<DevToolLayout>{children}</DevToolLayout>
</RootProviders>
</body>

View File

@@ -37,6 +37,7 @@ export default async function DashboardPage() {
return (
<Page style={'custom'}>
<PageHeader
displaySidebarTrigger={false}
title={'Dev Tool'}
description={'Kit MCP status for this workspace'}
/>

View File

@@ -1,4 +1,4 @@
import * as z from 'zod';
import { z } from 'zod';
export const CreatePRDSchema = z.object({
title: z
@@ -32,4 +32,4 @@ export const CreatePRDSchema = z.object({
.min(1, 'At least one success metric is required'),
});
export type CreatePRDData = z.output<typeof CreatePRDSchema>;
export type CreatePRDData = z.infer<typeof CreatePRDSchema>;

View File

@@ -131,14 +131,12 @@ export function TranslationsComparison({
<If condition={locales.length > 1}>
<DropdownMenu>
<DropdownMenuTrigger
render={
<Button variant="outline" className="ml-auto">
Select Languages
<ChevronDownIcon className="ml-2 h-4 w-4" />
</Button>
}
/>
<DropdownMenuTrigger asChild>
<Button variant="outline" className="ml-auto">
Select Languages
<ChevronDownIcon className="ml-2 h-4 w-4" />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end" className="w-[200px]">
{locales.map((locale) => (

View File

@@ -2,7 +2,7 @@
import { revalidatePath } from 'next/cache';
import * as z from 'zod';
import { z } from 'zod';
import { findWorkspaceRoot } from '@kit/mcp-server/env';
import {

View File

@@ -731,15 +731,13 @@ function FilterSwitcher(props: {
return (
<DropdownMenu>
<DropdownMenuTrigger
render={
<Button variant="outline" className="font-normal">
{buttonLabel()}
<DropdownMenuTrigger asChild>
<Button variant="outline" className="font-normal">
{buttonLabel()}
<ChevronsUpDownIcon className="text-muted-foreground ml-1 h-3 w-3" />
</Button>
}
/>
<ChevronsUpDownIcon className="text-muted-foreground ml-1 h-3 w-3" />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent>
<DropdownMenuCheckboxItem
@@ -888,41 +886,38 @@ function Summary({ appState }: { appState: AppEnvState }) {
<TooltipProvider>
<Tooltip>
<TooltipTrigger
render={
<Button
variant="outline"
size={'sm'}
onClick={() => {
let data = '';
<TooltipTrigger asChild>
<Button
variant="outline"
size={'sm'}
onClick={() => {
let data = '';
const groups = getGroups(appState, () => true);
const groups = getGroups(appState, () => true);
groups.forEach((group) => {
data += `# ${group.category}\n`;
groups.forEach((group) => {
data += `# ${group.category}\n`;
group.variables.forEach((variable) => {
data += `${variable.key}=${variable.effectiveValue}\n`;
});
data += '\n';
group.variables.forEach((variable) => {
data += `${variable.key}=${variable.effectiveValue}\n`;
});
const promise = copyToClipboard(data);
data += '\n';
});
toast.promise(promise, {
loading: 'Copying environment variables...',
success: 'Environment variables copied to clipboard.',
error:
'Failed to copy environment variables to clipboard',
});
}}
>
<CopyIcon className={'mr-2 h-4 w-4'} />
<span>Copy env file to clipboard</span>
</Button>
}
/>
const promise = copyToClipboard(data);
toast.promise(promise, {
loading: 'Copying environment variables...',
success: 'Environment variables copied to clipboard.',
error: 'Failed to copy environment variables to clipboard',
});
}}
>
<CopyIcon className={'mr-2 h-4 w-4'} />
<span>Copy env file to clipboard</span>
</Button>
</TooltipTrigger>
<TooltipContent>
Copy environment variables to clipboard. You can place it in your

View File

@@ -2,7 +2,7 @@
import { revalidatePath } from 'next/cache';
import * as z from 'zod';
import { z } from 'zod';
import {
createKitEnvDeps,

View File

@@ -1,6 +1,6 @@
import { DevToolSidebar } from '@/components/app-sidebar';
import { SidebarInset, SidebarProvider } from '@kit/ui/sidebar';
import { SidebarInset, SidebarProvider } from '@kit/ui/shadcn-sidebar';
export function DevToolLayout(props: React.PropsWithChildren) {
return (

View File

@@ -24,7 +24,7 @@ import {
SidebarMenuSub,
SidebarMenuSubButton,
SidebarMenuSubItem,
} from '@kit/ui/sidebar';
} from '@kit/ui/shadcn-sidebar';
import { isRouteActive } from '@kit/ui/utils';
const routes = [
@@ -92,14 +92,14 @@ export function DevToolSidebar({
{route.children.map((child) => (
<SidebarMenuSubItem key={child.path}>
<SidebarMenuSubButton
render={
<Link href={child.path}>
<child.Icon className="h-4 w-4" />
<span>{child.label}</span>
</Link>
}
asChild
isActive={isRouteActive(child.path, pathname, false)}
/>
>
<Link href={child.path}>
<child.Icon className="h-4 w-4" />
<span>{child.label}</span>
</Link>
</SidebarMenuSubButton>
</SidebarMenuSubItem>
))}
</SidebarMenuSub>
@@ -107,13 +107,13 @@ export function DevToolSidebar({
) : (
<SidebarMenuButton
isActive={isRouteActive(route.path, pathname, false)}
render={
<Link href={route.path}>
<route.Icon className="h-4 w-4" />
<span>{route.label}</span>
</Link>
}
/>
asChild
>
<Link href={route.path}>
<route.Icon className="h-4 w-4" />
<span>{route.label}</span>
</Link>
</SidebarMenuButton>
)}
</SidebarMenuItem>
))}

View File

@@ -3,18 +3,18 @@
import { useState } from 'react';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import type { AbstractIntlMessages } from 'next-intl';
import { I18nClientProvider } from '@kit/i18n/provider';
import { I18nProvider } from '@kit/i18n/provider';
import { Toaster } from '@kit/ui/sonner';
export function RootProviders(
props: React.PropsWithChildren<{ messages: AbstractIntlMessages }>,
) {
import { i18nResolver } from '../lib/i18n/i18n.resolver';
import { getI18nSettings } from '../lib/i18n/i18n.settings';
export function RootProviders(props: React.PropsWithChildren) {
return (
<I18nClientProvider locale="en" messages={props.messages}>
<I18nProvider settings={getI18nSettings('en')} resolver={i18nResolver}>
<ReactQueryProvider>{props.children}</ReactQueryProvider>
</I18nClientProvider>
</I18nProvider>
);
}

View File

@@ -33,7 +33,7 @@ interface ServiceCardProps {
export const ServiceCard = ({ name, status }: ServiceCardProps) => {
return (
<Card className="w-full max-w-2xl">
<CardContent>
<CardContent className="p-4">
<div className="space-y-4">
<div className="flex items-center justify-between">
<div className="flex items-center space-x-4">

View File

@@ -1,26 +0,0 @@
import { getRequestConfig } from 'next-intl/server';
import account from '../../web/i18n/messages/en/account.json';
import auth from '../../web/i18n/messages/en/auth.json';
import billing from '../../web/i18n/messages/en/billing.json';
import common from '../../web/i18n/messages/en/common.json';
import marketing from '../../web/i18n/messages/en/marketing.json';
import teams from '../../web/i18n/messages/en/teams.json';
export default getRequestConfig(async () => {
return {
locale: 'en',
messages: {
common,
auth,
account,
teams,
billing,
marketing,
},
timeZone: 'UTC',
getMessageFallback(info) {
return info.key;
},
};
});

View File

@@ -0,0 +1,13 @@
import { createI18nServerInstance } from './i18n.server';
type LayoutOrPageComponent<Params> = React.ComponentType<Params>;
export function withI18n<Params extends object>(
Component: LayoutOrPageComponent<Params>,
) {
return async function I18nServerComponentWrapper(params: Params) {
await createI18nServerInstance();
return <Component {...params} />;
};
}

View File

@@ -1,12 +1,8 @@
import type { NextConfig } from 'next';
import createNextIntlPlugin from 'next-intl/plugin';
const withNextIntl = createNextIntlPlugin('./i18n/request.ts');
const nextConfig: NextConfig = {
reactStrictMode: true,
transpilePackages: ['@kit/ui', '@kit/shared', '@kit/i18n'],
transpilePackages: ['@kit/ui', '@kit/shared'],
reactCompiler: true,
devIndicators: {
position: 'bottom-right',
@@ -18,4 +14,4 @@ const nextConfig: NextConfig = {
},
};
export default withNextIntl(nextConfig);
export default nextConfig;

View File

@@ -13,7 +13,6 @@
"@tanstack/react-query": "catalog:",
"lucide-react": "catalog:",
"next": "catalog:",
"next-intl": "catalog:",
"nodemailer": "catalog:",
"react": "catalog:",
"react-dom": "catalog:",
@@ -36,7 +35,7 @@
"babel-plugin-react-compiler": "1.0.0",
"pino-pretty": "13.0.0",
"react-hook-form": "catalog:",
"recharts": "3.7.0",
"recharts": "2.15.3",
"tailwindcss": "catalog:",
"tw-animate-css": "catalog:",
"typescript": "^5.9.3",

View File

@@ -66,6 +66,26 @@
--animate-accordion-down: accordion-down 0.2s ease-out;
--animate-accordion-up: accordion-up 0.2s ease-out;
@keyframes accordion-down {
from {
height: 0;
}
to {
height: var(--radix-accordion-content-height);
}
}
@keyframes accordion-up {
from {
height: var(--radix-accordion-content-height);
}
to {
height: 0;
}
}
@keyframes fade-up {
0% {
opacity: 0;