howl/src/button.tsx
2025-11-29 22:58:09 -08:00

148 lines
3.9 KiB
TypeScript

import "hono/jsx"
import type { JSX, FC } from "hono/jsx"
import { VStack, HStack } from "./stack"
import { Section } from "./section"
import { H2 } from "./text"
import { CodeExamples } from "./code"
export type ButtonProps = JSX.IntrinsicElements["button"] & {
variant?: "primary" | "secondary" | "outline" | "ghost" | "destructive"
size?: "sm" | "md" | "lg"
}
export const Button: FC<ButtonProps> = (props) => {
const { variant = "primary", size = "md", style, ...buttonProps } = props
const baseStyles: JSX.CSSProperties = {
display: "inline-flex",
alignItems: "center",
justifyContent: "center",
fontWeight: "500",
transition: "all 0.2s",
outline: "none",
cursor: "pointer",
borderRadius: "4px",
border: "1px solid transparent",
}
const variantStyles: Record<string, JSX.CSSProperties> = {
primary: {
backgroundColor: "#3b82f6",
color: "#ffffff",
},
secondary: {
backgroundColor: "#64748b",
color: "#ffffff",
},
outline: {
backgroundColor: "transparent",
color: "#000000",
borderColor: "#d1d5db",
},
ghost: {
backgroundColor: "transparent",
color: "#000000",
},
destructive: {
backgroundColor: "#ef4444",
color: "#ffffff",
},
}
const sizeStyles: Record<string, JSX.CSSProperties> = {
sm: {
height: "32px",
padding: "0 12px",
fontSize: "14px",
},
md: {
height: "40px",
padding: "0 16px",
fontSize: "14px",
},
lg: {
height: "48px",
padding: "0 24px",
fontSize: "16px",
},
}
const combinedStyles: JSX.CSSProperties = {
...baseStyles,
...variantStyles[variant],
...sizeStyles[size],
...(style as JSX.CSSProperties),
}
return <button {...buttonProps} style={combinedStyles} />
}
export const Test = () => {
return (
<Section>
{/* API Usage Examples */}
<CodeExamples
examples={[
'<Button>Click Me</Button>',
'<Button variant="secondary">Secondary</Button>',
'<Button variant="outline" size="lg">Large</Button>',
'<Button onClick={handleClick}>Action</Button>',
]}
/>
{/* Variants */}
<VStack gap={4}>
<H2>Button Variants</H2>
<HStack gap={4}>
<Button variant="primary">Primary</Button>
<Button variant="secondary">Secondary</Button>
<Button variant="outline">Outline</Button>
<Button variant="ghost">Ghost</Button>
<Button variant="destructive">Destructive</Button>
</HStack>
</VStack>
{/* Sizes */}
<VStack gap={4}>
<H2>Button Sizes</H2>
<HStack gap={4} v="end">
<Button size="sm">Small</Button>
<Button size="md">Medium</Button>
<Button size="lg">Large</Button>
</HStack>
</VStack>
{/* With custom content */}
<VStack gap={4}>
<H2>Custom Content</H2>
<HStack gap={4}>
<Button variant="primary">
<span>🚀</span>
<span style={{ marginLeft: "8px" }}>Launch</span>
</Button>
<Button variant="outline" style={{ flexDirection: "column", height: "80px", width: "96px" }}>
<span style={{ fontSize: "24px" }}>💳</span>
<span style={{ fontSize: "12px", marginTop: "4px" }}>Card</span>
</Button>
</HStack>
</VStack>
{/* Native attributes work */}
<VStack gap={4}>
<H2>Native Attributes</H2>
<HStack gap={4}>
<Button onClick={() => alert("Clicked!")} variant="primary">
Click Me
</Button>
<Button disabled variant="secondary" style={{ opacity: 0.5, pointerEvents: "none" }}>
Disabled
</Button>
<Button type="submit" variant="outline">
Submit
</Button>
</HStack>
</VStack>
</Section>
)
}