melt
Version:
The next generation of Melt UI. Built for Svelte 5.
58 lines (57 loc) • 1.89 kB
JavaScript
import { extract } from "../utils/extract";
import { createDataIds } from "../utils/identifiers";
import { styleAttr } from "../utils/attribute";
import { inBrowser } from "../utils/browser";
import { watch } from "runed";
import { createAttachmentKey } from "svelte/attachments";
const identifiers = createDataIds("avatar", ["image", "fallback"]);
export class Avatar {
/* Props */
#props;
src = $derived(extract(this.#props.src, ""));
delayMs = $derived(extract(this.#props.delayMs, 0));
/* State */
#loadingStatus = $state("loading");
#ak = createAttachmentKey();
#setLoadingStatus(s) {
this.#loadingStatus = s;
this.#props.onLoadingStatusChange?.(s);
}
constructor(props = {}) {
this.#props = props;
}
get loadingStatus() {
return this.#loadingStatus;
}
#attach = () => {
watch(() => this.src, () => {
this.#setLoadingStatus("loading");
});
};
get image() {
return {
[identifiers.image]: "",
src: this.src,
style: styleAttr({ display: this.#loadingStatus === "loaded" ? "block" : "none" }),
onload: () => {
if (!inBrowser())
return;
const timerId = window.setTimeout(() => {
this.#setLoadingStatus("loaded");
}, this.delayMs);
return () => window.clearTimeout(timerId);
},
onerror: () => {
this.#setLoadingStatus("error");
},
[this.#ak]: this.#attach,
};
}
get fallback() {
return {
[identifiers.fallback]: "",
style: this.#loadingStatus === "loaded" ? styleAttr({ display: "none" }) : undefined,
hidden: this.#loadingStatus === "loaded" ? true : undefined,
};
}
}