Files
beavergram/frontend/src/lib/components/ui/Avatar.svelte
T

105 lines
2.2 KiB
Svelte

<script lang="ts">
import { type AvatarKind, loadAvatar } from "#lib/api/avatars.js";
import { initials as toInitials } from "#lib/format/peer.js";
interface Props {
avatar?: { account?: number; kind: AvatarKind; id: number } | null;
colorKey?: number;
deleted?: boolean;
hasAvatar?: boolean;
name: string;
size?: number;
}
let {
name,
colorKey = 0,
size = 3.375,
deleted = false,
avatar = null,
hasAvatar = false,
}: Props = $props();
const gradients = [
["#ff885e", "#ff516a"],
["#ffcd6a", "#ffa85c"],
["#82b1ff", "#665fff"],
["#a0de7e", "#54cb68"],
["#53edd6", "#28c9b7"],
["#72d5fd", "#2a9ef1"],
["#e0a2f3", "#d669ed"],
];
const pair = $derived(gradients[Math.abs(colorKey) % gradients.length]);
const initials = $derived(toInitials(name));
let url = $state<string | null>(null);
$effect(() => {
url = null;
if (deleted || !(hasAvatar && avatar)) {
return;
}
let active = true;
loadAvatar(avatar.kind, avatar.id, avatar.account).then((resolved) => {
if (active) {
url = resolved;
}
});
return () => {
active = false;
};
});
</script>
<div
class="Avatar"
style="
--avatar-size: {size}rem;
--avatar-from: {pair[0]};
--avatar-to: {pair[1]};
"
class:deleted
>
{#if url}
<img alt={name} class="photo" src={url}>
{:else}
<span class="initials">{initials}</span>
{/if}
</div>
<style lang="scss">
.Avatar {
position: relative;
display: flex;
flex-shrink: 0;
align-items: center;
justify-content: center;
overflow: hidden;
width: var(--avatar-size);
height: var(--avatar-size);
border-radius: 50%;
font-size: calc(var(--avatar-size) * 0.4);
font-weight: var(--font-weight-medium);
line-height: 1;
color: var(--color-white);
text-transform: uppercase;
user-select: none;
background-image: linear-gradient(var(--avatar-from), var(--avatar-to));
&.deleted {
background-image: none;
background-color: var(--color-deleted-account);
}
}
.photo {
width: 100%;
height: 100%;
object-fit: cover;
}
</style>