152 lines
5.1 KiB
JavaScript
152 lines
5.1 KiB
JavaScript
"use client";
|
|
|
|
// src/avatar.tsx
|
|
import * as React from "react";
|
|
import { createContextScope } from "@radix-ui/react-context";
|
|
import { useCallbackRef } from "@radix-ui/react-use-callback-ref";
|
|
import { useLayoutEffect } from "@radix-ui/react-use-layout-effect";
|
|
import { Primitive } from "@radix-ui/react-primitive";
|
|
import { jsx } from "react/jsx-runtime";
|
|
var AVATAR_NAME = "Avatar";
|
|
var [createAvatarContext, createAvatarScope] = createContextScope(AVATAR_NAME);
|
|
var STATIC_IMAGE_COUNT_STATE = [
|
|
0,
|
|
() => void 0
|
|
];
|
|
var [AvatarProvider, useAvatarContext] = createAvatarContext(AVATAR_NAME);
|
|
var Avatar = React.forwardRef(
|
|
(props, forwardedRef) => {
|
|
const { __scopeAvatar, ...avatarProps } = props;
|
|
const [imageLoadingStatus, setImageLoadingStatus] = React.useState("idle");
|
|
const [imageCount, setImageCount] = useImageCount();
|
|
return /* @__PURE__ */ jsx(
|
|
AvatarProvider,
|
|
{
|
|
scope: __scopeAvatar,
|
|
imageLoadingStatus,
|
|
setImageLoadingStatus,
|
|
imageCount,
|
|
setImageCount,
|
|
children: /* @__PURE__ */ jsx(Primitive.span, { ...avatarProps, ref: forwardedRef })
|
|
}
|
|
);
|
|
}
|
|
);
|
|
Avatar.displayName = AVATAR_NAME;
|
|
var IMAGE_NAME = "AvatarImage";
|
|
var AvatarImage = React.forwardRef(
|
|
(props, forwardedRef) => {
|
|
const { __scopeAvatar, src, onLoadingStatusChange, ...imageProps } = props;
|
|
const context = useAvatarContext(IMAGE_NAME, __scopeAvatar);
|
|
useUpdateImageCount(context.setImageCount);
|
|
const imageLoadingStatus = useImageLoadingStatus(src, {
|
|
referrerPolicy: imageProps.referrerPolicy,
|
|
crossOrigin: imageProps.crossOrigin,
|
|
loadingStatus: context.imageLoadingStatus,
|
|
setLoadingStatus: context.setImageLoadingStatus
|
|
});
|
|
const handleLoadingStatusChange = useCallbackRef((status) => {
|
|
onLoadingStatusChange?.(status);
|
|
});
|
|
const loadingStatusRef = React.useRef(imageLoadingStatus);
|
|
useLayoutEffect(() => {
|
|
const previousLoadingStatus = loadingStatusRef.current;
|
|
loadingStatusRef.current = imageLoadingStatus;
|
|
if (imageLoadingStatus !== previousLoadingStatus) {
|
|
handleLoadingStatusChange(imageLoadingStatus);
|
|
}
|
|
}, [imageLoadingStatus, handleLoadingStatusChange]);
|
|
return imageLoadingStatus === "loaded" ? /* @__PURE__ */ jsx(Primitive.img, { ...imageProps, ref: forwardedRef, src }) : null;
|
|
}
|
|
);
|
|
AvatarImage.displayName = IMAGE_NAME;
|
|
var FALLBACK_NAME = "AvatarFallback";
|
|
var AvatarFallback = React.forwardRef(
|
|
(props, forwardedRef) => {
|
|
const { __scopeAvatar, delayMs, ...fallbackProps } = props;
|
|
const context = useAvatarContext(FALLBACK_NAME, __scopeAvatar);
|
|
const [canRender, setCanRender] = React.useState(delayMs === void 0);
|
|
React.useEffect(() => {
|
|
if (delayMs !== void 0) {
|
|
const timerId = window.setTimeout(() => setCanRender(true), delayMs);
|
|
return () => window.clearTimeout(timerId);
|
|
}
|
|
}, [delayMs]);
|
|
return canRender && context.imageLoadingStatus !== "loaded" ? /* @__PURE__ */ jsx(Primitive.span, { ...fallbackProps, ref: forwardedRef }) : null;
|
|
}
|
|
);
|
|
AvatarFallback.displayName = FALLBACK_NAME;
|
|
function useImageLoadingStatus(src, {
|
|
loadingStatus,
|
|
setLoadingStatus,
|
|
referrerPolicy,
|
|
crossOrigin
|
|
}) {
|
|
useLayoutEffect(() => {
|
|
if (!src) {
|
|
setLoadingStatus("error");
|
|
return;
|
|
}
|
|
const image = new window.Image();
|
|
const handleLoad = (event) => {
|
|
const image2 = event.currentTarget;
|
|
setLoadingStatus(getImageLoadingStatus(image2));
|
|
};
|
|
const handleError = () => setLoadingStatus("error");
|
|
image.addEventListener("load", handleLoad);
|
|
image.addEventListener("error", handleError);
|
|
if (referrerPolicy) {
|
|
image.referrerPolicy = referrerPolicy;
|
|
}
|
|
image.crossOrigin = crossOrigin ?? null;
|
|
image.src = src;
|
|
setLoadingStatus(getImageLoadingStatus(image));
|
|
return () => {
|
|
image.removeEventListener("load", handleLoad);
|
|
image.removeEventListener("error", handleError);
|
|
setLoadingStatus("idle");
|
|
};
|
|
}, [src, crossOrigin, referrerPolicy, setLoadingStatus]);
|
|
return loadingStatus;
|
|
}
|
|
function getImageLoadingStatus(image) {
|
|
return image.complete ? image.naturalWidth > 0 ? "loaded" : "error" : "loading";
|
|
}
|
|
function useImageCount() {
|
|
let state = STATIC_IMAGE_COUNT_STATE;
|
|
if (true) {
|
|
state = React.useState(0);
|
|
const [imageCount] = state;
|
|
const hasWarnedRef = React.useRef(false);
|
|
React.useEffect(() => {
|
|
if (imageCount > 1 && !hasWarnedRef.current) {
|
|
hasWarnedRef.current = true;
|
|
console.warn(
|
|
"Avatar: Only one `Avatar.Image` component should be rendered per `Avatar.Root`, but multiple were detected. This will lead to unexpected behavior."
|
|
);
|
|
}
|
|
}, [imageCount]);
|
|
}
|
|
return state;
|
|
}
|
|
function useUpdateImageCount(setImageCount) {
|
|
if (true) {
|
|
React.useEffect(() => {
|
|
setImageCount((imageCount) => imageCount + 1);
|
|
return () => {
|
|
setImageCount((imageCount) => imageCount - 1);
|
|
};
|
|
}, [setImageCount]);
|
|
}
|
|
}
|
|
export {
|
|
Avatar,
|
|
AvatarFallback,
|
|
AvatarImage,
|
|
AvatarFallback as Fallback,
|
|
AvatarImage as Image,
|
|
Avatar as Root,
|
|
createAvatarScope
|
|
};
|
|
//# sourceMappingURL=index.mjs.map
|