forked from defunkt/howl
/ |/ \ / \ / \ / | $$$$$$$$//$$$$$$ |$$$$$$$ |/$$$$$$ |$$$$$$$$/ $$ |__ $$ | $$ |$$ |__$$ |$$ | _$$/ $$ |__ $$ | $$ | $$ |$$ $$< $$ |/ |$$ | $$$$$/ $$ | $$ |$$$$$$$ |$$ |$$$$ |$$$$$/ $$ | $$ \__$$ |$$ | $$ |$$ \__$$ |$$ |_____ $$ | $$ $$/ $$ | $$ |$$ $$/ $$ | $$/ $$$$$$/ $$/ $$/ $$$$$$/ $$$$$$$$/
245 lines
7.1 KiB
TypeScript
245 lines
7.1 KiB
TypeScript
import { Section } from './section'
|
|
import { H2, H3, Text, SmallText } from './text'
|
|
import { Avatar } from './avatar'
|
|
import type { AvatarProps } from './avatar'
|
|
import { Image } from './image'
|
|
import type { ImageProps } from './image'
|
|
import { VStack, HStack } from './stack'
|
|
import { Grid } from './grid'
|
|
|
|
export const Placeholder = {
|
|
Avatar(props: PlaceholderAvatarProps) {
|
|
const { size = 32, seed = 'seed', type = 'dylan', transparent, alt, style, rounded, id, ref, class: className } = props
|
|
|
|
// Generate DiceBear avatar URL
|
|
const url = new URL(`https://api.dicebear.com/9.x/${type}/svg`)
|
|
url.searchParams.set('seed', seed)
|
|
url.searchParams.set('size', size.toString())
|
|
|
|
if (transparent) {
|
|
url.searchParams.set('backgroundColor', 'transparent')
|
|
}
|
|
|
|
return <Avatar src={url.toString()} alt={alt} style={style} size={size as any} rounded={rounded} id={id} ref={ref} class={className} />
|
|
},
|
|
|
|
Image(props: PlaceholderImageProps) {
|
|
const { width = 200, height = 200, seed = 1, alt = 'Placeholder image', objectFit, style, id, ref, class: className } = props
|
|
|
|
// Generate Picsum Photos URL with seed for consistent images
|
|
const src = `https://picsum.photos/${width}/${height}?random=${seed}`
|
|
|
|
return <Image src={src} alt={alt} objectFit={objectFit} width={width} height={height} style={style} id={id} ref={ref} class={className} />
|
|
},
|
|
}
|
|
|
|
export const Test = () => {
|
|
return (
|
|
<Section>
|
|
{/* Show all available avatar styles */}
|
|
<VStack gap={4}>
|
|
<H2>
|
|
All Avatar Styles ({allStyles.length} total)
|
|
</H2>
|
|
<Grid cols={6} gap={3}>
|
|
{allStyles.slice(0, 12).map((style) => (
|
|
<VStack h="center" gap={1} key={style}>
|
|
<Placeholder.Avatar type={style} size={48} />
|
|
<SmallText style={{ fontWeight: '500' }}>{style}</SmallText>
|
|
</VStack>
|
|
))}
|
|
</Grid>
|
|
</VStack>
|
|
|
|
{/* Avatar size variations */}
|
|
<VStack gap={4}>
|
|
<H2>Avatar Size Variations</H2>
|
|
<HStack gap={4}>
|
|
{[24, 32, 48, 64].map((size) => (
|
|
<VStack h="center" gap={2} key={size}>
|
|
<Placeholder.Avatar size={size} />
|
|
<Text>{size}px</Text>
|
|
</VStack>
|
|
))}
|
|
</HStack>
|
|
</VStack>
|
|
|
|
{/* Avatar styling combinations */}
|
|
<VStack gap={4}>
|
|
<H2>Avatar Styling Options</H2>
|
|
<HStack gap={6}>
|
|
<VStack h="center" gap={2}>
|
|
<Placeholder.Avatar rounded size={64} />
|
|
<Text>Rounded + Background</Text>
|
|
</VStack>
|
|
<VStack h="center" gap={2}>
|
|
<div style={{ backgroundColor: '#e5e7eb', padding: '8px' }}>
|
|
<Placeholder.Avatar rounded transparent size={64} />
|
|
</div>
|
|
<Text>Rounded + Transparent</Text>
|
|
</VStack>
|
|
<VStack h="center" gap={2}>
|
|
<Placeholder.Avatar size={64} />
|
|
<Text>Square + Background</Text>
|
|
</VStack>
|
|
<VStack h="center" gap={2}>
|
|
<div style={{ backgroundColor: '#e5e7eb', padding: '8px' }}>
|
|
<Placeholder.Avatar transparent size={64} />
|
|
</div>
|
|
<Text>Square + Transparent</Text>
|
|
</VStack>
|
|
</HStack>
|
|
</VStack>
|
|
|
|
{/* Avatar seed variations */}
|
|
<VStack gap={4}>
|
|
<H2>
|
|
Avatar Seeds (Same Style, Different People)
|
|
</H2>
|
|
<HStack gap={4}>
|
|
{['alice', 'bob', 'charlie', 'diana'].map((seed) => (
|
|
<VStack h="center" gap={2} key={seed}>
|
|
<Placeholder.Avatar seed={seed} size={64} />
|
|
<Text>"{seed}"</Text>
|
|
</VStack>
|
|
))}
|
|
</HStack>
|
|
</VStack>
|
|
|
|
{/* Placeholder Images */}
|
|
<VStack gap={6}>
|
|
<H2>Placeholder Images</H2>
|
|
|
|
{/* Size variations */}
|
|
<VStack gap={3}>
|
|
<H3>Size Variations</H3>
|
|
<HStack gap={4}>
|
|
{[
|
|
{ width: 100, height: 100 },
|
|
{ width: 150, height: 100 },
|
|
{ width: 200, height: 150 },
|
|
{ width: 250, height: 200 },
|
|
].map(({ width, height }) => (
|
|
<VStack h="center" gap={2} key={`${width}x${height}`}>
|
|
<Placeholder.Image width={width} height={height} seed={1} />
|
|
<Text>
|
|
{width}x{height}
|
|
</Text>
|
|
</VStack>
|
|
))}
|
|
</HStack>
|
|
</VStack>
|
|
|
|
{/* Different seeds - show variety */}
|
|
<VStack gap={3}>
|
|
<H3>
|
|
Different Images (Different Seeds)
|
|
</H3>
|
|
<HStack gap={4}>
|
|
{[1, 2, 3, 4, 5].map((seed) => (
|
|
<VStack h="center" gap={2} key={seed}>
|
|
<Placeholder.Image width={150} height={150} seed={seed} />
|
|
<Text>Seed {seed}</Text>
|
|
</VStack>
|
|
))}
|
|
</HStack>
|
|
</VStack>
|
|
|
|
{/* With custom styles */}
|
|
<VStack gap={3}>
|
|
<H3>With Custom Styles</H3>
|
|
<HStack gap={6}>
|
|
<VStack h="center" gap={2}>
|
|
<Placeholder.Image
|
|
width={150}
|
|
height={150}
|
|
seed={1}
|
|
objectFit="cover"
|
|
style={{ borderRadius: '8px', border: '4px solid #3b82f6' }}
|
|
/>
|
|
<Text>Rounded + Border</Text>
|
|
</VStack>
|
|
<VStack h="center" gap={2}>
|
|
<Placeholder.Image
|
|
width={150}
|
|
height={150}
|
|
seed={2}
|
|
objectFit="cover"
|
|
style={{ boxShadow: '0 10px 15px rgba(0, 0, 0, 0.3)' }}
|
|
/>
|
|
<Text>With Shadow</Text>
|
|
</VStack>
|
|
<VStack h="center" gap={2}>
|
|
<Placeholder.Image
|
|
width={150}
|
|
height={150}
|
|
seed={3}
|
|
objectFit="cover"
|
|
style={{
|
|
borderRadius: '9999px',
|
|
border: '4px solid #22c55e',
|
|
boxShadow: '0 10px 15px rgba(0, 0, 0, 0.3)',
|
|
}}
|
|
/>
|
|
<Text>Circular + Effects</Text>
|
|
</VStack>
|
|
</HStack>
|
|
</VStack>
|
|
</VStack>
|
|
</Section>
|
|
)
|
|
}
|
|
|
|
// Type definitions
|
|
type PlaceholderAvatarProps = Omit<AvatarProps, 'src'> & {
|
|
seed?: string
|
|
type?: DicebearStyleName
|
|
transparent?: boolean
|
|
}
|
|
|
|
type PlaceholderImageProps = Omit<ImageProps, 'src' | 'alt'> & {
|
|
width?: number
|
|
height?: number
|
|
seed?: number
|
|
alt?: string
|
|
}
|
|
|
|
// All supported DiceBear HTTP styleNames. Source: https://www.dicebear.com/styles
|
|
const allStyles = [
|
|
'adventurer',
|
|
'adventurer-neutral',
|
|
'avataaars',
|
|
'avataaars-neutral',
|
|
'big-ears',
|
|
'big-ears-neutral',
|
|
'big-smile',
|
|
'bottts',
|
|
'bottts-neutral',
|
|
'croodles',
|
|
'croodles-neutral',
|
|
'dylan',
|
|
'fun-emoji',
|
|
'glass',
|
|
'icons',
|
|
'identicon',
|
|
'initials',
|
|
'lorelei',
|
|
'lorelei-neutral',
|
|
'micah',
|
|
'miniavs',
|
|
'notionists',
|
|
'notionists-neutral',
|
|
'open-peeps',
|
|
'personas',
|
|
'pixel-art',
|
|
'pixel-art-neutral',
|
|
'rings',
|
|
'shapes',
|
|
'thumbs',
|
|
] as const
|
|
|
|
type DicebearStyleName = (typeof allStyles)[number]
|
|
|
|
// Default export for convenience
|
|
export default Placeholder
|