2026-01-12 14:13:21 -06:00
|
|
|
import type { Avatar } from "../../protos/avatar";
|
2026-01-14 12:34:28 -06:00
|
|
|
import type { SkeletonProps } from "../../types/SkeletonProps";
|
2026-01-12 14:13:21 -06:00
|
|
|
import { Text } from "../Text";
|
|
|
|
|
import "./index.css";
|
|
|
|
|
|
2026-01-14 13:58:53 -06:00
|
|
|
interface Props {
|
|
|
|
|
name: string;
|
|
|
|
|
avatars: Avatar[];
|
|
|
|
|
}
|
2026-01-13 13:30:09 -06:00
|
|
|
|
2026-01-14 13:58:53 -06:00
|
|
|
const MAX_CARDS = 3;
|
|
|
|
|
const CARD_TRIM = "var(--space-3)";
|
|
|
|
|
|
|
|
|
|
export function AvatarCard(props: SkeletonProps<Props>) {
|
2026-01-12 14:13:21 -06:00
|
|
|
return (
|
|
|
|
|
<div className="avatar-card">
|
2026-01-14 13:58:53 -06:00
|
|
|
{!props.skeleton && <Series avatars={props.avatars} />}
|
|
|
|
|
|
2026-01-13 13:30:09 -06:00
|
|
|
<Text align="center" className="avatar-label">
|
2026-01-14 13:58:53 -06:00
|
|
|
{props.skeleton ? "LOADING" : props.name}
|
2026-01-13 13:30:09 -06:00
|
|
|
</Text>
|
2026-01-12 14:13:21 -06:00
|
|
|
</div>
|
|
|
|
|
);
|
|
|
|
|
}
|
2026-01-14 13:58:53 -06:00
|
|
|
|
|
|
|
|
interface SeriesProps {
|
|
|
|
|
avatars: Avatar[];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function Series({ avatars }: SeriesProps) {
|
|
|
|
|
const count = Math.min(avatars.length, MAX_CARDS);
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
<div className="avatar-series">
|
|
|
|
|
{avatars.slice(-MAX_CARDS).map((avatar, index) => (
|
|
|
|
|
<div
|
|
|
|
|
className="avatar-entry"
|
|
|
|
|
style={{
|
|
|
|
|
filter: `brightness(${(index + 1) / count})`,
|
|
|
|
|
top: `calc(${index} * ${CARD_TRIM})`,
|
|
|
|
|
left: `calc(${index} * ${CARD_TRIM})`,
|
|
|
|
|
width: `calc(100% - ${CARD_TRIM} * ${count - 1})`,
|
|
|
|
|
height: `calc(100% - ${CARD_TRIM} * ${count - 1})`,
|
|
|
|
|
backgroundImage: `url(/api/avatars/${avatar.name}.webp)`,
|
|
|
|
|
}}
|
|
|
|
|
/>
|
|
|
|
|
))}
|
|
|
|
|
</div>
|
|
|
|
|
);
|
|
|
|
|
}
|