howl/src/image.tsx
2025-11-29 22:44:51 -08:00

230 lines
7.2 KiB
TypeScript

import { Section } from "./section"
import { H2, H3, H4, H5, Text, SmallText } from "./text"
import "hono/jsx"
import type { FC, JSX } from "hono/jsx"
import { VStack, HStack } from "./stack"
import { Grid } from "./grid"
export type ImageProps = {
src: string
alt?: string
width?: number
height?: number
objectFit?: "cover" | "contain" | "fill" | "none" | "scale-down"
style?: JSX.CSSProperties
}
export const Image: FC<ImageProps> = ({ src, alt = "", width, height, objectFit, style }) => {
const imageStyle: JSX.CSSProperties = {
width: width ? `${width}px` : undefined,
height: height ? `${height}px` : undefined,
objectFit: objectFit,
...style,
}
return <img src={src} alt={alt} style={imageStyle} />
}
export const Test = () => {
const sampleImages = [
"https://picsum.photos/seed/1/400/600", // Portrait
"https://picsum.photos/seed/2/600/400", // Landscape
"https://picsum.photos/seed/3/300/300", // Square
"https://picsum.photos/seed/4/200/100", // Small image
]
return (
<Section>
{/* API Usage Examples */}
<VStack gap={2} style={{ backgroundColor: "#f9fafb", padding: "16px", borderRadius: "8px", fontFamily: "monospace", fontSize: "12px" }}>
<div>&lt;Image src="/photo.jpg" /&gt;</div>
<div>&lt;Image src="/photo.jpg" width=&#123;200&#125; height=&#123;200&#125; /&gt;</div>
<div>&lt;Image src="/photo.jpg" objectFit="cover" /&gt;</div>
<div>&lt;Image src="/photo.jpg" style=&#123;&#123; borderRadius: "8px" &#125;&#125; /&gt;</div>
</VStack>
<H2>Image Examples</H2>
{/* Size variations */}
<VStack gap={4}>
<H3>Size Variations</H3>
<HStack gap={4} wrap>
<VStack h="center" gap={2}>
<Image src={sampleImages[0]!} width={64} height={64} objectFit="cover" alt="64x64" />
<Text>64x64</Text>
</VStack>
<VStack h="center" gap={2}>
<Image src={sampleImages[0]!} width={96} height={96} objectFit="cover" alt="96x96" />
<Text>96x96</Text>
</VStack>
<VStack h="center" gap={2}>
<Image src={sampleImages[0]!} width={128} height={128} objectFit="cover" alt="128x128" />
<Text>128x128</Text>
</VStack>
<VStack h="center" gap={2}>
<Image src={sampleImages[0]!} width={192} height={128} objectFit="cover" alt="192x128" />
<Text>192x128</Text>
</VStack>
</HStack>
</VStack>
{/* Object fit variations */}
<VStack gap={4}>
<H3>Object Fit Variations</H3>
<Text>
Same image with different object-fit values
</Text>
<HStack gap={6} wrap>
<VStack h="center" gap={2}>
<Image
src={sampleImages[0]!}
width={128}
height={128}
objectFit="cover"
style={{ border: "1px solid black" }}
alt="Object cover"
/>
<Text>object-fit: cover</Text>
</VStack>
<VStack h="center" gap={2}>
<Image
src={sampleImages[0]!}
width={128}
height={128}
objectFit="contain"
style={{ border: "1px solid black", backgroundColor: "#f3f4f6" }}
alt="Object contain"
/>
<Text>object-fit: contain</Text>
</VStack>
<VStack h="center" gap={2}>
<Image
src={sampleImages[0]!}
width={128}
height={128}
objectFit="fill"
style={{ border: "1px solid black" }}
alt="Object fill"
/>
<Text>object-fit: fill</Text>
</VStack>
<VStack h="center" gap={2}>
<Image
src={sampleImages[0]!}
width={128}
height={128}
objectFit="scale-down"
style={{ border: "1px solid black", backgroundColor: "#f3f4f6" }}
alt="Object scale-down"
/>
<Text>object-fit: scale-down</Text>
</VStack>
<VStack h="center" gap={2}>
<Image
src={sampleImages[0]!}
width={128}
height={128}
objectFit="none"
style={{ border: "1px solid black", backgroundColor: "#f3f4f6" }}
alt="Object none"
/>
<Text>object-fit: none</Text>
</VStack>
</HStack>
</VStack>
{/* Styling examples */}
<VStack gap={4}>
<H3>Styling Examples</H3>
<HStack gap={6} wrap>
<VStack h="center" gap={2}>
<Image
src={sampleImages[0]!}
width={128}
height={128}
objectFit="cover"
style={{ borderRadius: "8px", border: "4px solid #3b82f6" }}
alt="Rounded with border"
/>
<Text>Rounded + Border</Text>
</VStack>
<VStack h="center" gap={2}>
<Image
src={sampleImages[1]!}
width={128}
height={128}
objectFit="cover"
style={{ boxShadow: "0 10px 15px rgba(0, 0, 0, 0.3)" }}
alt="With shadow"
/>
<Text>With Shadow</Text>
</VStack>
<VStack h="center" gap={2}>
<Image
src={sampleImages[2]!}
width={128}
height={128}
objectFit="cover"
style={{
borderRadius: "9999px",
border: "4px solid #22c55e",
boxShadow: "0 10px 15px rgba(0, 0, 0, 0.3)",
}}
alt="Circular with effects"
/>
<Text>Circular + Effects</Text>
</VStack>
</HStack>
</VStack>
{/* Common use cases */}
<VStack gap={6}>
<H3>Common Use Cases</H3>
{/* Avatar */}
<VStack gap={2}>
<H4>Avatar</H4>
<Image
src={sampleImages[0]!}
width={48}
height={48}
objectFit="cover"
style={{ borderRadius: "9999px" }}
alt="Avatar"
/>
</VStack>
{/* Card image */}
<VStack gap={2} style={{ maxWidth: "384px" }}>
<H4>Card Image</H4>
<VStack gap={0} style={{ border: "1px solid #d1d5db", borderRadius: "8px", overflow: "hidden" }}>
<Image src={sampleImages[1]!} width={384} height={192} objectFit="cover" alt="Card image" />
<VStack gap={1} style={{ padding: "16px" }}>
<H5>Card Title</H5>
<Text>Card description goes here</Text>
</VStack>
</VStack>
</VStack>
{/* Gallery grid */}
<VStack gap={2}>
<H4>Gallery Grid</H4>
<Grid cols={3} gap={2}>
{sampleImages.map((src, i) => (
<Image
key={i}
src={src}
width={120}
height={120}
objectFit="cover"
style={{ borderRadius: "4px" }}
alt={`Gallery ${i}`}
/>
))}
</Grid>
</VStack>
</VStack>
</Section>
)
}