ai-wpa/templates/component.template.tsx

152 lines
3.9 KiB
TypeScript

/**
* Component Template
*
* Usage: Copy this file to create new components
* 1. Replace TEMPLATE_NAME with your component name
* 2. Define the props interface
* 3. Implement your component logic
*/
'use client'
import { useState } from 'react'
import { cn } from '@/lib/utils'
// Component props interface
interface TemplateComponentProps {
// Define your props here
className?: string
children?: React.ReactNode
// Add other props as needed
title?: string
description?: string
variant?: 'default' | 'secondary' | 'outline'
size?: 'sm' | 'md' | 'lg'
disabled?: boolean
onClick?: () => void
}
/**
* TEMPLATE_NAME Component
*
* @param props - Component properties
* @returns JSX element
*/
export function TemplateComponent({
className,
children,
title,
description,
variant = 'default',
size = 'md',
disabled = false,
onClick,
...props
}: TemplateComponentProps) {
// State management
const [isLoading, setIsLoading] = useState(false)
// Event handlers
const handleClick = async () => {
if (disabled || isLoading) return
setIsLoading(true)
try {
await onClick?.()
} finally {
setIsLoading(false)
}
}
// CSS class variations
const variants = {
default: 'bg-primary text-primary-foreground hover:bg-primary/90',
secondary: 'bg-secondary text-secondary-foreground hover:bg-secondary/80',
outline: 'border border-input bg-background hover:bg-accent hover:text-accent-foreground',
}
const sizes = {
sm: 'h-8 px-3 text-sm',
md: 'h-10 px-4 py-2',
lg: 'h-12 px-8 text-lg',
}
return (
<div
className={cn(
// Base styles
'inline-flex items-center justify-center rounded-md font-medium transition-colors',
'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2',
'disabled:pointer-events-none disabled:opacity-50',
// Variants
variants[variant],
sizes[size],
// Loading state
isLoading && 'cursor-not-allowed opacity-70',
className
)}
onClick={handleClick}
{...props}
>
{/* Loading spinner */}
{isLoading && (
<svg
className="mr-2 h-4 w-4 animate-spin"
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
>
<circle
className="opacity-25"
cx="12"
cy="12"
r="10"
stroke="currentColor"
strokeWidth="4"
/>
<path
className="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"
/>
</svg>
)}
{/* Content */}
<div className="flex flex-col items-center">
{title && <h3 className="font-semibold">{title}</h3>}
{description && <p className="text-sm text-muted-foreground">{description}</p>}
{children}
</div>
</div>
)
}
/**
* Alternative component patterns:
*
* 1. Server Component (remove 'use client' and useState)
* 2. Compound Components:
*
* export function TemplateComponent({ children }: { children: React.ReactNode }) {
* return <div className="template-wrapper">{children}</div>;
* }
*
* TemplateComponent.Header = function Header({ children }: { children: React.ReactNode }) {
* return <header className="template-header">{children}</header>;
* };
*
* TemplateComponent.Body = function Body({ children }: { children: React.ReactNode }) {
* return <main className="template-body">{children}</main>;
* };
*
* 3. Forward Ref:
*
* export const TemplateComponent = forwardRef<HTMLDivElement, TemplateComponentProps>(
* ({ className, ...props }, ref) => {
* return <div ref={ref} className={cn('template-base', className)} {...props} />;
* }
* );
* TemplateComponent.displayName = "TemplateComponent";
*/