Modern UI components built with Astro
Components สมัยใหม่ที่สร้างด้วย Astro
โดย Astrobeginner Team 1 นาทีในการอ่าน development

การสร้าง Components สมัยใหม่ด้วย Astro และ TypeScript

เรียนรู้วิธีการสร้าง UI components ที่มีประสิทธิภาพและนำกลับมาใช้ได้ด้วย Astro และ TypeScript

#astro #typescript #components #ui

การสร้าง Components สมัยใหม่ด้วย Astro และ TypeScript

การสร้าง components ที่ดีเป็นรากฐานสำคัญของการพัฒนาเว็บไซต์สมัยใหม่ ในบทความนี้เราจะมาเรียนรู้วิธีการสร้าง components ด้วย Astro และ TypeScript กัน

ทำไมต้องใช้ TypeScript?

TypeScript ช่วยให้เราสามารถ:

  • Type Safety: ป้องกันข้อผิดพลาดได้ตั้งแต่เวลาเขียนโค้ด
  • Better IntelliSense: ได้รับการช่วยเหลือจาก IDE มากขึ้น
  • Self-documenting: โค้ดอธิบายตัวเองได้ดีขึ้น
  • Refactoring: เปลี่ยนแปลงโค้ดได้อย่างมั่นใจ

การสร้าง Button Component

เริ่มต้นด้วย Button component ที่มี variants หลายแบบ:

---
// Button.astro
export interface Props {
  variant?: 'primary' | 'secondary' | 'outline' | 'ghost';
  size?: 'sm' | 'md' | 'lg';
  disabled?: boolean;
  loading?: boolean;
  href?: string;
  type?: 'button' | 'submit' | 'reset';
  class?: string;
}

const {
  variant = 'primary',
  size = 'md',
  disabled = false,
  loading = false,
  href,
  type = 'button',
  class: className = '',
  ...rest
} = Astro.props;

const baseClasses = 'inline-flex items-center justify-center font-medium transition-colors focus:outline-none focus:ring-2 focus:ring-offset-2 disabled:opacity-50 disabled:pointer-events-none';

const variantClasses = {
  primary: 'bg-blue-600 text-white hover:bg-blue-700 focus:ring-blue-500',
  secondary: 'bg-gray-600 text-white hover:bg-gray-700 focus:ring-gray-500',
  outline: 'border border-gray-300 bg-transparent hover:bg-gray-50 text-gray-700 focus:ring-gray-500',
  ghost: 'bg-transparent hover:bg-gray-100 text-gray-700 focus:ring-gray-500'
};

const sizeClasses = {
  sm: 'px-3 py-1.5 text-sm rounded',
  md: 'px-4 py-2 text-base rounded-md',
  lg: 'px-6 py-3 text-lg rounded-lg'
};

const classes = [
  baseClasses,
  variantClasses[variant],
  sizeClasses[size],
  className
].join(' ');

const Element = href ? 'a' : 'button';
---

<Element
  class={classes}
  disabled={disabled || loading}
  type={href ? undefined : type}
  href={href}
  {...rest}
>
  {loading && (
    <svg class="animate-spin -ml-1 mr-2 h-4 w-4" fill="none" viewBox="0 0 24 24">
      <circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
      <path class="opacity-75" fill="currentColor" d="m4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
    </svg>
  )}
  <slot />
</Element>

การสร้าง Card Component

Card component ที่ใช้งานได้หลากหลาย:

---
// Card.astro
export interface Props {
  variant?: 'default' | 'elevated' | 'outlined';
  padding?: 'none' | 'sm' | 'md' | 'lg';
  class?: string;
}

const {
  variant = 'default',
  padding = 'md',
  class: className = ''
} = Astro.props;

const baseClasses = 'bg-white rounded-lg transition-all duration-200';

const variantClasses = {
  default: 'shadow-sm border border-gray-200',
  elevated: 'shadow-lg hover:shadow-xl',
  outlined: 'border-2 border-gray-200 hover:border-gray-300'
};

const paddingClasses = {
  none: '',
  sm: 'p-4',
  md: 'p-6',
  lg: 'p-8'
};

const classes = [
  baseClasses,
  variantClasses[variant],
  paddingClasses[padding],
  className
].join(' ');
---

<div class={classes}>
  <slot />
</div>

การใช้งาน Components

ใช้งาน components ที่เราสร้างขึ้น:

---
// pages/components-demo.astro
import Button from '../components/Button.astro';
import Card from '../components/Card.astro';
---

<html>
  <head>
    <title>Components Demo</title>
  </head>
  <body>
    <div class="container mx-auto px-4 py-8">
      <h1 class="text-3xl font-bold mb-8">Components Demo</h1>
      
      <!-- Button Examples -->
      <Card class="mb-8">
        <h2 class="text-xl font-semibold mb-4">Buttons</h2>
        <div class="space-x-4">
          <Button variant="primary">Primary</Button>
          <Button variant="secondary">Secondary</Button>
          <Button variant="outline">Outline</Button>
          <Button variant="ghost">Ghost</Button>
          <Button loading={true}>Loading</Button>
        </div>
      </Card>
      
      <!-- Card Examples -->
      <div class="grid grid-cols-1 md:grid-cols-3 gap-6">
        <Card variant="default">
          <h3 class="font-semibold mb-2">Default Card</h3>
          <p class="text-gray-600">This is a default card with shadow.</p>
        </Card>
        
        <Card variant="elevated">
          <h3 class="font-semibold mb-2">Elevated Card</h3>
          <p class="text-gray-600">This card has elevated shadow.</p>
        </Card>
        
        <Card variant="outlined">
          <h3 class="font-semibold mb-2">Outlined Card</h3>
          <p class="text-gray-600">This card has a border outline.</p>
        </Card>
      </div>
    </div>
  </body>
</html>

Best Practices

1. Interface Definition

export interface Props {
  // Required props ไม่ต้องมี default value
  title: string;
  
  // Optional props ควรมี default value
  variant?: 'primary' | 'secondary';
  disabled?: boolean;
  
  // HTML attributes
  class?: string;
  id?: string;
}

2. Props Destructuring

---
const {
  title,
  variant = 'primary',
  disabled = false,
  class: className = '',
  ...rest
} = Astro.props;
---

3. Conditional Classes

const classes = [
  'base-class',
  variant === 'primary' ? 'primary-class' : 'secondary-class',
  disabled && 'disabled-class',
  className
].filter(Boolean).join(' ');

การทดสอบ Components

สร้างหน้าทดสอบสำหรับ components:

---
// pages/component-tests.astro
import Button from '../components/Button.astro';

const buttonVariants = ['primary', 'secondary', 'outline', 'ghost'] as const;
const buttonSizes = ['sm', 'md', 'lg'] as const;
---

<html>
  <head>
    <title>Component Tests</title>
  </head>
  <body>
    <div class="container mx-auto px-4 py-8">
      <h1 class="text-3xl font-bold mb-8">Component Tests</h1>
      
      {buttonVariants.map(variant => (
        <div class="mb-6">
          <h2 class="text-xl font-semibold mb-4 capitalize">{variant} Buttons</h2>
          <div class="space-x-4">
            {buttonSizes.map(size => (
              <Button variant={variant} size={size}>
                {variant} {size}
              </Button>
            ))}
          </div>
        </div>
      ))}
    </div>
  </body>
</html>

สรุป

การสร้าง components ที่ดีด้วย Astro และ TypeScript ต้องคำนึงถึง:

  1. Type Safety: ใช้ TypeScript interfaces สำหรับ props
  2. Reusability: สร้าง components ที่ใช้งานได้หลากหลาย
  3. Consistency: ใช้ design system และ naming conventions ที่สม่ำเสมอ
  4. Performance: ใช้ CSS classes แทน inline styles
  5. Accessibility: เพิ่ม ARIA attributes ที่จำเป็น

ด้วยหลักการเหล่านี้ เราสามารถสร้าง component library ที่มีคุณภาพและนำกลับมาใช้ได้อย่างมีประสิทธิภาพ!

แชร์บทความนี้

🐦 Twitter 📘 Facebook

เกี่ยวกับผู้เขียน

A

Astrobeginner Team

ทีมงาน Astrobeginner ที่หลงใหลในการพัฒนาเว็บไซต์สมัยใหม่ด้วย Astro และเทคโนโลยีต่างๆ

บทความที่เกี่ยวข้อง