package.dist.index.mjs Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of avatar Show documentation
Show all versions of avatar Show documentation
Core logic for the avatar widget implemented as a state machine
The newest version!
import { createAnatomy } from '@zag-js/anatomy';
import { createScope, observeAttributes, observeChildren } from '@zag-js/dom-query';
import { createMachine } from '@zag-js/core';
import { createSplitProps, compact } from '@zag-js/utils';
import { createProps } from '@zag-js/types';
// src/avatar.anatomy.ts
var anatomy = createAnatomy("avatar").parts("root", "image", "fallback");
var parts = anatomy.build();
var dom = createScope({
getRootId: (ctx) => ctx.ids?.root ?? `avatar:${ctx.id}`,
getImageId: (ctx) => ctx.ids?.image ?? `avatar:${ctx.id}:image`,
getFallbackId: (ctx) => ctx.ids?.fallback ?? `avatar:${ctx.id}:fallback`,
getRootEl: (ctx) => dom.getById(ctx, dom.getRootId(ctx)),
getImageEl: (ctx) => dom.getById(ctx, dom.getImageId(ctx))
});
// src/avatar.connect.ts
function connect(state, send, normalize) {
const loaded = state.matches("loaded");
return {
loaded,
setSrc(src) {
send({ type: "SRC.SET", src });
},
setLoaded() {
send({ type: "IMG.LOADED", src: "api" });
},
setError() {
send({ type: "IMG.ERROR", src: "api" });
},
getRootProps() {
return normalize.element({
...parts.root.attrs,
dir: state.context.dir,
id: dom.getRootId(state.context)
});
},
getImageProps() {
return normalize.img({
...parts.image.attrs,
hidden: !loaded,
dir: state.context.dir,
id: dom.getImageId(state.context),
"data-state": loaded ? "visible" : "hidden",
onLoad() {
send({ type: "IMG.LOADED", src: "element" });
},
onError() {
send({ type: "IMG.ERROR", src: "element" });
}
});
},
getFallbackProps() {
return normalize.element({
...parts.fallback.attrs,
dir: state.context.dir,
id: dom.getFallbackId(state.context),
hidden: loaded,
"data-state": loaded ? "hidden" : "visible"
});
}
};
}
function machine(userContext) {
const ctx = compact(userContext);
return createMachine(
{
id: "avatar",
initial: "loading",
activities: ["trackImageRemoval"],
context: ctx,
on: {
"SRC.CHANGE": {
target: "loading"
},
"IMG.UNMOUNT": {
target: "error"
}
},
states: {
loading: {
activities: ["trackSrcChange"],
entry: ["checkImageStatus"],
on: {
"IMG.LOADED": {
target: "loaded",
actions: ["invokeOnLoad"]
},
"IMG.ERROR": {
target: "error",
actions: ["invokeOnError"]
}
}
},
error: {
activities: ["trackSrcChange"],
on: {
"IMG.LOADED": {
target: "loaded",
actions: ["invokeOnLoad"]
}
}
},
loaded: {
activities: ["trackSrcChange"],
on: {
"IMG.ERROR": {
target: "error",
actions: ["invokeOnError"]
}
}
}
}
},
{
activities: {
trackSrcChange(ctx2, _evt, { send }) {
const imageEl = dom.getImageEl(ctx2);
return observeAttributes(imageEl, {
attributes: ["src", "srcset"],
callback() {
send({ type: "SRC.CHANGE" });
}
});
},
trackImageRemoval(ctx2, _evt, { send }) {
const rootEl = dom.getRootEl(ctx2);
return observeChildren(rootEl, {
callback(records) {
const removedNodes = Array.from(records[0].removedNodes);
const removed = removedNodes.find(
(node) => node.nodeType === Node.ELEMENT_NODE && node.matches("[data-scope=avatar][data-part=image]")
);
if (removed) {
send({ type: "IMG.UNMOUNT" });
}
}
});
}
},
actions: {
invokeOnLoad(ctx2) {
ctx2.onStatusChange?.({ status: "loaded" });
},
invokeOnError(ctx2) {
ctx2.onStatusChange?.({ status: "error" });
},
checkImageStatus(ctx2, _evt, { send }) {
const imageEl = dom.getImageEl(ctx2);
if (imageEl?.complete) {
const type = hasLoaded(imageEl) ? "IMG.LOADED" : "IMG.ERROR";
send({ type, src: "ssr" });
}
}
}
}
);
}
function hasLoaded(image) {
return image.complete && image.naturalWidth !== 0 && image.naturalHeight !== 0;
}
var props = createProps()(["dir", "id", "ids", "onStatusChange", "getRootNode"]);
var splitProps = createSplitProps(props);
export { anatomy, connect, machine, props, splitProps };
© 2015 - 2025 Weber Informatics LLC | Privacy Policy