ai

Shadcn UI กับ Automation Script — วิธีสร้าง UI

Shadcn UI กับ Automation Script — วิธีสร้าง UI

Shadcn UI คืออะไร

Shadcn UI กับ Automation Script — วิธีสร้าง UI

Shadcn UI เป็น Component Library แนวใหม่ที่ไม่ได้ติดตั้งเป็น npm package แต่ Copy Source Code ลงในโปรเจคโดยตรง ทำให้ Developer มี Full Control สามารถปรับแต่ง Component ได้ตามต้องการ สร้างบน Radix UI Primitives (Accessible) และ Tailwind CSS (Styling)

การใช้ Automation Scripts ร่วมกับ Shadcn UI ช่วยเพิ่มประสิทธิภาพในการ Setup Project, Generate Components หลายตัวพร้อมกัน, Customize Theme อัตโนมัติ และสร้าง Documentation จาก Components

เนื้อหาเกี่ยวข้อง — ทำความเข้าใจ LLM Inference vLLM Event Driven Design —

Automation Script — Setup และ Generate Components

#!/bin/bash
# shadcn-setup.sh — Automation Script สำหรับ Shadcn UI Setup
# ใช้กับ Next.js + TypeScript + Tailwind CSS

set -e

PROJECT_NAME=
echo "=== Setting up Shadcn UI Project: $PROJECT_NAME ==="

# 1. สร้าง Next.js Project
npx create-next-app@latest "$PROJECT_NAME" \
  --typescript --tailwind --eslint --app \
  --src-dir --import-alias "@/*" --no-git

cd "$PROJECT_NAME"

# 2. Initialize Shadcn UI
npx shadcn-ui@latest init -y \
  --style default \
  --base-color slate \
  --css-variables

# 3. ติดตั้ง Essential Components
COMPONENTS=(
  "button"
  "input"
  "label"
  "card"
  "dialog"
  "dropdown-menu"
  "table"
  "tabs"
  "toast"
  "form"
  "select"
  "badge"
  "alert"
  "avatar"
  "separator"
  "skeleton"
  "sheet"
  "command"
  "popover"
  "tooltip"
)

echo "Installing  components..."
for comp in ""; do
  echo "  Adding: $comp"
  npx shadcn-ui@latest add "$comp" -y 2>/dev/null || echo "  Skipped: $comp"
done

# 4. ติดตั้ง Dependencies เพิ่มเติม
npm install lucide-react @tanstack/react-table date-fns zod \
  @hookform/resolvers react-hook-form next-themes

# 5. สร้าง Theme Provider
mkdir -p src/components
cat > src/components/theme-provider.tsx << 'THEME_EOF'
"use client"
import { ThemeProvider as NextThemesProvider } from "next-themes"
import { type ThemeProviderProps } from "next-themes/dist/types"

export function ThemeProvider({ children, ...props }: ThemeProviderProps) {
  return {children}
}
THEME_EOF

# 6. สร้าง Toaster Component
cat > src/components/toaster.tsx << 'TOAST_EOF'
"use client"
import { Toaster as SonnerToaster } from "sonner"

export function Toaster() {
  return 
}
TOAST_EOF

echo ""
echo "=== Setup Complete ==="
echo "Components installed: "
echo "Run: cd $PROJECT_NAME && npm run dev"

Python Script — Component Generator

# shadcn_generator.py — Generate Custom Components จาก Template import os import json import subprocess from pathlib import Path from dataclasses import dataclass from typing import List, Optional @dataclass class ComponentSpec: name: str description: str props: dict shadcn_deps: List[str] template: str = "default" class ShadcnComponentGenerator: """Generate Custom Components บน Shadcn UI""" def __init__(self, project_path): self.project_path = Path(project_path) self.components_dir = self.project_path / "src" / "components" self.ui_dir = self.components_dir / "ui" def ensure_shadcn_deps(self, deps: List[str]): """ติดตั้ง Shadcn Components ที่จำเป็น""" for dep in deps: dep_file = self.ui_dir / f"{dep}.tsx" if not dep_file.exists(): print(f" Installing shadcn component: {dep}") subprocess.run( ["npx", "shadcn-ui@latest", "add", dep, "-y"], cwd=str(self.project_path), capture_output=True, ) def generate_data_table(self, name="data-table", columns=None): """Generate Data Table Component""" self.ensure_shadcn_deps(["table", "button", "input", "dropdown-menu", "badge"]) component_code = ''' "use client" import * as React from "react" import { ColumnDef, ColumnFiltersState, SortingState, VisibilityState, flexRender, getCoreRowModel, getFilteredRowModel, getPaginationRowModel, getSortedRowModel, useReactTable, } from "@tanstack/react-table" import { ArrowUpDown, ChevronDown, MoreHorizontal } from "lucide-react" import { Button } from "@/components/ui/button" import { Input } from "@/components/ui/input" import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow, } from "@/components/ui/table" import { DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuTrigger, } from "@/components/ui/dropdown-menu" interface DataTableProps { columns: ColumnDef[] data: TData[] searchKey?: string searchPlaceholder?: string } export function DataTable({ columns, data, searchKey, searchPlaceholder = "Search...", }: DataTableProps) { const [sorting, setSorting] = React.useState([]) const [columnFilters, setColumnFilters] = React.useState([]) 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 (
{searchKey && ( table.getColumn(searchKey) ?.setFilterValue(e.target.value)} className="max-w-sm" /> )} {table.getAllColumns() .filter((col) => col.getCanHide()) .map((col) => ( col.toggleVisibility(!!v)} > {col.id} ))}
{table.getHeaderGroups().map((hg) => ( {hg.headers.map((h) => ( {h.isPlaceholder ? null : flexRender(h.column.columnDef.header, h.getContext())} ))} ))} {table.getRowModel().rows?.length ? ( table.getRowModel().rows.map((row) => ( {row.getVisibleCells().map((cell) => ( {flexRender(cell.column.columnDef.cell, cell.getContext())} ))} )) ) : ( No results. )}
{table.getFilteredSelectedRowModel().rows.length} of{" "} {table.getFilteredRowModel().rows.length} row(s) selected.
) } ''' output_path = self.components_dir / f"{name}.tsx" output_path.write_text(component_code.strip(), encoding="utf-8") print(f"Generated: {output_path}") def generate_component_index(self): """สร้าง Index File สำหรับ Export ทุก Components""" ui_files = sorted(self.ui_dir.glob("*.tsx")) exports = [] for f in ui_files: name = f.stem exports.append(f'export * from "./ui/{name}"') index_path = self.components_dir / "index.ts" index_path.write_text("\n".join(exports), encoding="utf-8") print(f"Generated index: {index_path} ({len(exports)} exports)") def audit_accessibility(self): """ตรวจสอบ Accessibility ของ Components""" issues = [] for f in self.ui_dir.glob("*.tsx"): content = f.read_text(encoding="utf-8", errors="ignore") # Check for aria labels if "
XM Legend · เทรดเดอร์ & ผู้สอน Forex 13 ปี

ผู้ก่อตั้ง SiamCafe ตั้งแต่ปี 1997 · เทรดเดอร์สาย Forex มากกว่า 13 ปี ได้รับการยกย่องเป็น XM Legend · แบ่งปันความรู้ Forex, ไอที, AI และการเทรด จากประสบการณ์จริงในตลาดจริง