初始化项目版本

This commit is contained in:
Axhub Make
2026-07-29 16:04:39 +08:00
commit 4305a1082b
2629 changed files with 760590 additions and 0 deletions

View File

@@ -0,0 +1,177 @@
// Example: Authentication Layout with shadcn/ui
// Demonstrates: Layout composition, card usage, form integration
"use client"
import { Button } from "@/components/ui/button"
import {
Card,
CardContent,
CardDescription,
CardFooter,
CardHeader,
CardTitle,
} from "@/components/ui/card"
import { Input } from "@/components/ui/input"
import { Label } from "@/components/ui/label"
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"
import { useState } from "react"
export function AuthLayout() {
const [isLoading, setIsLoading] = useState<boolean>(false)
async function onSubmit(event: React.FormEvent<HTMLFormElement>) {
event.preventDefault()
setIsLoading(true)
// Simulate API call
setTimeout(() => {
setIsLoading(false)
}, 2000)
}
return (
<div className="flex min-h-screen items-center justify-center bg-muted/40">
<Tabs defaultValue="login" className="w-[400px]">
<TabsList className="grid w-full grid-cols-2">
<TabsTrigger value="login">Login</TabsTrigger>
<TabsTrigger value="register">Register</TabsTrigger>
</TabsList>
<TabsContent value="login">
<Card>
<CardHeader>
<CardTitle>Login</CardTitle>
<CardDescription>
Enter your credentials to access your account.
</CardDescription>
</CardHeader>
<form onSubmit={onSubmit}>
<CardContent className="space-y-4">
<div className="space-y-2">
<Label htmlFor="email">Email</Label>
<Input
id="email"
type="email"
placeholder="m@example.com"
required
/>
</div>
<div className="space-y-2">
<Label htmlFor="password">Password</Label>
<Input
id="password"
type="password"
required
/>
</div>
</CardContent>
<CardFooter className="flex flex-col space-y-4">
<Button
type="submit"
className="w-full"
disabled={isLoading}
>
{isLoading ? "Signing in..." : "Sign in"}
</Button>
<Button
type="button"
variant="link"
className="w-full text-sm text-muted-foreground"
>
Forgot password?
</Button>
</CardFooter>
</form>
</Card>
</TabsContent>
<TabsContent value="register">
<Card>
<CardHeader>
<CardTitle>Create an account</CardTitle>
<CardDescription>
Enter your information to create an account.
</CardDescription>
</CardHeader>
<form onSubmit={onSubmit}>
<CardContent className="space-y-4">
<div className="space-y-2">
<Label htmlFor="name">Name</Label>
<Input
id="name"
placeholder="John Doe"
required
/>
</div>
<div className="space-y-2">
<Label htmlFor="register-email">Email</Label>
<Input
id="register-email"
type="email"
placeholder="m@example.com"
required
/>
</div>
<div className="space-y-2">
<Label htmlFor="register-password">Password</Label>
<Input
id="register-password"
type="password"
required
/>
</div>
<div className="space-y-2">
<Label htmlFor="confirm-password">Confirm Password</Label>
<Input
id="confirm-password"
type="password"
required
/>
</div>
</CardContent>
<CardFooter>
<Button
type="submit"
className="w-full"
disabled={isLoading}
>
{isLoading ? "Creating account..." : "Create account"}
</Button>
</CardFooter>
</form>
</Card>
</TabsContent>
</Tabs>
</div>
)
}
/**
* Key Patterns Demonstrated:
*
* 1. Layout Composition: Centered authentication card with full-height viewport
* 2. Card Usage: Structured content with header, body, and footer
* 3. Tabs: Switch between login and register forms
* 4. Form Structure: Proper labeling and input grouping
* 5. Loading States: Button disabled state during form submission
* 6. Responsive Design: Mobile-friendly with max-width constraint
* 7. Tailwind Utilities: Using spacing, flexbox, and grid utilities
*
* Design Choices:
* - Minimal, clean interface focusing on the task at hand
* - Proper semantic HTML with form elements
* - Accessible labels and inputs
* - Clear visual hierarchy with card components
* - Loading feedback for better UX
*
* Required Dependencies:
* None beyond React and shadcn/ui components
*
* Installation:
* npx shadcn@latest add card
* npx shadcn@latest add input
* npx shadcn@latest add label
* npx shadcn@latest add button
* npx shadcn@latest add tabs
*/

View File

@@ -0,0 +1,313 @@
// Example: Data Table with Sorting and Filtering
// Demonstrates: Table composition, TanStack Table integration, responsive design
"use client"
import {
ColumnDef,
ColumnFiltersState,
flexRender,
getCoreRowModel,
getFilteredRowModel,
getPaginationRowModel,
getSortedRowModel,
SortingState,
useReactTable,
} from "@tanstack/react-table"
import { ArrowUpDown, ChevronDown, MoreHorizontal } from "lucide-react"
import * as React from "react"
import { Button } from "@/components/ui/button"
import {
DropdownMenu,
DropdownMenuCheckboxItem,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuLabel,
DropdownMenuSeparator,
DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu"
import { Input } from "@/components/ui/input"
import {
Table,
TableBody,
TableCell,
TableHead,
TableHeader,
TableRow,
} from "@/components/ui/table"
// Define data type
export type User = {
id: string
name: string
email: string
role: "admin" | "user" | "viewer"
status: "active" | "inactive"
}
// Sample data
const data: User[] = [
{
id: "1",
name: "Alice Johnson",
email: "alice@example.com",
role: "admin",
status: "active",
},
{
id: "2",
name: "Bob Smith",
email: "bob@example.com",
role: "user",
status: "active",
},
{
id: "3",
name: "Carol White",
email: "carol@example.com",
role: "viewer",
status: "inactive",
},
]
// Define columns
export const columns: ColumnDef<User>[] = [
{
accessorKey: "name",
header: ({ column }) => {
return (
<Button
variant="ghost"
onClick={() => column.toggleSorting(column.getIsSorted() === "asc")}
>
Name
<ArrowUpDown className="ml-2 h-4 w-4" />
</Button>
)
},
cell: ({ row }) => <div className="capitalize">{row.getValue("name")}</div>,
},
{
accessorKey: "email",
header: ({ column }) => {
return (
<Button
variant="ghost"
onClick={() => column.toggleSorting(column.getIsSorted() === "asc")}
>
Email
<ArrowUpDown className="ml-2 h-4 w-4" />
</Button>
)
},
cell: ({ row }) => <div className="lowercase">{row.getValue("email")}</div>,
},
{
accessorKey: "role",
header: "Role",
cell: ({ row }) => (
<div className="capitalize">{row.getValue("role")}</div>
),
},
{
accessorKey: "status",
header: "Status",
cell: ({ row }) => (
<div className="capitalize">{row.getValue("status")}</div>
),
},
{
id: "actions",
enableHiding: false,
cell: ({ row }) => {
const user = row.original
return (
<DropdownMenu>
<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">
<DropdownMenuLabel>Actions</DropdownMenuLabel>
<DropdownMenuItem
onClick={() => navigator.clipboard.writeText(user.id)}
>
Copy user ID
</DropdownMenuItem>
<DropdownMenuSeparator />
<DropdownMenuItem>View user</DropdownMenuItem>
<DropdownMenuItem>Edit user</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
)
},
},
]
export function DataTableExample() {
const [sorting, setSorting] = React.useState<SortingState>([])
const [columnFilters, setColumnFilters] = React.useState<ColumnFiltersState>([])
const [columnVisibility, setColumnVisibility] = React.useState({})
const [rowSelection, setRowSelection] = React.useState({})
const table = useReactTable({
data,
columns,
onSortingChange: setSorting,
onColumnFiltersChange: setColumnFilters,
getCoreRowModel: getCoreRowModel(),
getPaginationRowModel: getPaginationRowModel(),
getSortedRowModel: getSortedRowModel(),
getFilteredRowModel: getFilteredRowModel(),
onColumnVisibilityChange: setColumnVisibility,
onRowSelectionChange: setRowSelection,
state: {
sorting,
columnFilters,
columnVisibility,
rowSelection,
},
})
return (
<div className="w-full">
<div className="flex items-center py-4">
<Input
placeholder="Filter names..."
value={(table.getColumn("name")?.getFilterValue() as string) ?? ""}
onChange={(event) =>
table.getColumn("name")?.setFilterValue(event.target.value)
}
className="max-w-sm"
/>
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant="outline" className="ml-auto">
Columns <ChevronDown className="ml-2 h-4 w-4" />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end">
{table
.getAllColumns()
.filter((column) => column.getCanHide())
.map((column) => {
return (
<DropdownMenuCheckboxItem
key={column.id}
className="capitalize"
checked={column.getIsVisible()}
onCheckedChange={(value) =>
column.toggleVisibility(!!value)
}
>
{column.id}
</DropdownMenuCheckboxItem>
)
})}
</DropdownMenuContent>
</DropdownMenu>
</div>
<div className="rounded-md border">
<Table>
<TableHeader>
{table.getHeaderGroups().map((headerGroup) => (
<TableRow key={headerGroup.id}>
{headerGroup.headers.map((header) => {
return (
<TableHead key={header.id}>
{header.isPlaceholder
? null
: flexRender(
header.column.columnDef.header,
header.getContext()
)}
</TableHead>
)
})}
</TableRow>
))}
</TableHeader>
<TableBody>
{table.getRowModel().rows?.length ? (
table.getRowModel().rows.map((row) => (
<TableRow
key={row.id}
data-state={row.getIsSelected() && "selected"}
>
{row.getVisibleCells().map((cell) => (
<TableCell key={cell.id}>
{flexRender(
cell.column.columnDef.cell,
cell.getContext()
)}
</TableCell>
))}
</TableRow>
))
) : (
<TableRow>
<TableCell
colSpan={columns.length}
className="h-24 text-center"
>
No results.
</TableCell>
</TableRow>
)}
</TableBody>
</Table>
</div>
<div className="flex items-center justify-end space-x-2 py-4">
<div className="flex-1 text-sm text-muted-foreground">
{table.getFilteredSelectedRowModel().rows.length} of{" "}
{table.getFilteredRowModel().rows.length} row(s) selected.
</div>
<div className="space-x-2">
<Button
variant="outline"
size="sm"
onClick={() => table.previousPage()}
disabled={!table.getCanPreviousPage()}
>
Previous
</Button>
<Button
variant="outline"
size="sm"
onClick={() => table.nextPage()}
disabled={!table.getCanNextPage()}
>
Next
</Button>
</div>
</div>
</div>
)
}
/**
* Key Patterns Demonstrated:
*
* 1. TanStack Table Integration: Using @tanstack/react-table with shadcn/ui
* 2. Sorting: Click headers to sort ascending/descending
* 3. Filtering: Text input to filter table data
* 4. Column Visibility: Toggle columns via dropdown menu
* 5. Pagination: Built-in pagination controls
* 6. Row Actions: Dropdown menu per row for context actions
* 7. Responsive Design: Table adapts to different screen sizes
*
* Required Dependencies:
* - @tanstack/react-table
* - lucide-react
*
* Installation:
* npx shadcn@latest add table
* npx shadcn@latest add button
* npx shadcn@latest add input
* npx shadcn@latest add dropdown-menu
* npm install @tanstack/react-table lucide-react
*/

View File

@@ -0,0 +1,177 @@
// Example: Form Pattern with shadcn/ui components
// Demonstrates: Form building, validation, and composition
"use client"
import { Button } from "@/components/ui/button"
import {
Form,
FormControl,
FormDescription,
FormField,
FormItem,
FormLabel,
FormMessage,
} from "@/components/ui/form"
import { Input } from "@/components/ui/input"
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "@/components/ui/select"
import { Textarea } from "@/components/ui/textarea"
import { toast } from "@/components/ui/use-toast"
import { zodResolver } from "@hookform/resolvers/zod"
import { useForm } from "react-hook-form"
import * as z from "zod"
// Define form schema with zod
const formSchema = z.object({
username: z.string().min(2, {
message: "Username must be at least 2 characters.",
}),
email: z.string().email({
message: "Please enter a valid email address.",
}),
role: z.enum(["admin", "user", "viewer"], {
required_error: "Please select a role.",
}),
bio: z.string().max(160, {
message: "Bio must not be longer than 160 characters.",
}).optional(),
})
type FormValues = z.infer<typeof formSchema>
export function UserProfileForm() {
// Initialize form with react-hook-form and zod validation
const form = useForm<FormValues>({
resolver: zodResolver(formSchema),
defaultValues: {
username: "",
email: "",
bio: "",
},
})
// Handle form submission
function onSubmit(values: FormValues) {
// In a real app, send to API
console.log(values)
toast({
title: "Profile updated",
description: "Your profile has been successfully updated.",
})
}
return (
<Form {...form}>
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-8">
<FormField
control={form.control}
name="username"
render={({ field }) => (
<FormItem>
<FormLabel>Username</FormLabel>
<FormControl>
<Input placeholder="johndoe" {...field} />
</FormControl>
<FormDescription>
This is your public display name.
</FormDescription>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="email"
render={({ field }) => (
<FormItem>
<FormLabel>Email</FormLabel>
<FormControl>
<Input type="email" placeholder="john@example.com" {...field} />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="role"
render={({ field }) => (
<FormItem>
<FormLabel>Role</FormLabel>
<Select onValueChange={field.onChange} defaultValue={field.value}>
<FormControl>
<SelectTrigger>
<SelectValue placeholder="Select a role" />
</SelectTrigger>
</FormControl>
<SelectContent>
<SelectItem value="admin">Admin</SelectItem>
<SelectItem value="user">User</SelectItem>
<SelectItem value="viewer">Viewer</SelectItem>
</SelectContent>
</Select>
<FormDescription>
Your role determines your access level.
</FormDescription>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="bio"
render={({ field }) => (
<FormItem>
<FormLabel>Bio</FormLabel>
<FormControl>
<Textarea
placeholder="Tell us about yourself"
className="resize-none"
{...field}
/>
</FormControl>
<FormDescription>
Optional. Maximum 160 characters.
</FormDescription>
<FormMessage />
</FormItem>
)}
/>
<Button type="submit">Update profile</Button>
</form>
</Form>
)
}
/**
* Key Patterns Demonstrated:
*
* 1. Form Composition: Using shadcn/ui's Form components with react-hook-form
* 2. Validation: Zod schema for type-safe validation
* 3. Error Handling: Automatic error messages via FormMessage
* 4. Accessibility: All fields properly labeled with descriptions
* 5. Type Safety: TypeScript types inferred from Zod schema
*
* Required Dependencies:
* - react-hook-form
* - @hookform/resolvers
* - zod
*
* Installation:
* npx shadcn@latest add form
* npx shadcn@latest add input
* npx shadcn@latest add select
* npx shadcn@latest add textarea
* npx shadcn@latest add button
*/