@trail-ui/react
Version:
145 lines (142 loc) • 4.75 kB
JavaScript
import {
AvatarIcon
} from "./chunk-B63AQA6W.mjs";
// src/avatar/avatar.tsx
import { useImage } from "@trail-ui/hooks";
import { clsx, dataAttr } from "@trail-ui/shared-utils";
import { avatar } from "@trail-ui/theme";
import { filterDOMProps } from "@react-aria/utils";
import {
createContext,
forwardRef,
useMemo
} from "react";
import { mergeProps, useFocusRing, useHover } from "react-aria";
import {
Button,
Tooltip,
TooltipTrigger,
useContextProps
} from "react-aria-components";
import { jsx, jsxs } from "react/jsx-runtime";
var safeText = (name) => {
const parts = name.trim().split(" ");
if (parts.length === 1) {
return parts[0].slice(0, 2).toUpperCase();
} else {
return (parts[0][0] + parts[1][0]).toUpperCase();
}
};
var AvatarContext = createContext({});
function Avatar(props, ref) {
[props, ref] = useContextProps(props, ref, AvatarContext);
const ctx = props;
const {
elementType,
src,
name,
icon = /* @__PURE__ */ jsx(AvatarIcon, {}),
className,
classNames,
fallback: fallbackComponent,
alt = name || "avatar",
color = "red",
radius = "full",
size = "md",
isBordered = false,
isDisabled = false,
ignoreFallback = false,
showFallback: showFallbackProp = false,
getInitials = safeText,
ImgComponent = "img",
imgProps,
onError,
onClick,
...avatarProps
} = props;
const Component = elementType || "span";
const { isFocusVisible, isFocused, focusProps } = useFocusRing();
const { isHovered, hoverProps } = useHover({ isDisabled });
const imageStatus = useImage({ src, onError, ignoreFallback });
const isImgLoaded = imageStatus === "loaded";
const showFallback = (!src || !isImgLoaded) && showFallbackProp;
const slots = useMemo(
() => {
var _a;
return avatar({
color,
radius,
size,
isBordered,
isDisabled,
isInGroup: ctx == null ? void 0 : ctx.isInGroup,
isInGridGroup: (_a = ctx == null ? void 0 : ctx.isGrid) != null ? _a : false
});
},
[color, radius, size, isBordered, isDisabled, ctx == null ? void 0 : ctx.isInGroup, ctx == null ? void 0 : ctx.isGrid]
);
const baseStyles = clsx(classNames == null ? void 0 : classNames.base, className);
const fallback = useMemo(() => {
if (!showFallback && src)
return null;
if (fallbackComponent) {
return /* @__PURE__ */ jsx(
"div",
{
"aria-label": alt,
className: slots.fallback({ class: classNames == null ? void 0 : classNames.fallback }),
role: "img",
children: fallbackComponent
}
);
}
return name ? /* @__PURE__ */ jsx("span", { "aria-label": alt, className: slots.name({ class: classNames == null ? void 0 : classNames.name }), role: "img", children: getInitials(name) }) : /* @__PURE__ */ jsx("span", { "aria-label": alt, className: slots.icon({ class: classNames == null ? void 0 : classNames.icon }), role: "img", children: icon });
}, [showFallback, src, fallbackComponent, classNames, name, icon, alt, slots, getInitials]);
return /* @__PURE__ */ jsxs(TooltipTrigger, { delay: 200, closeDelay: 200, children: [
/* @__PURE__ */ jsx(
Button,
{
isDisabled,
className: `${isDisabled ? "pointer-events-none cursor-default" : ""} focus-visible:rounded-full focus-visible:outline focus-visible:outline-2 focus-visible:outline-purple-600`,
onPress: onClick,
children: /* @__PURE__ */ jsxs(
Component,
{
...mergeProps(filterDOMProps(avatarProps), focusProps, hoverProps),
ref,
"data-hovered": dataAttr(isHovered),
"data-focused": dataAttr(isFocused),
"data-focus-visible": dataAttr(isFocusVisible),
className: slots.base({ class: baseStyles }),
children: [
src && /* @__PURE__ */ jsx(
ImgComponent,
{
src,
alt,
"data-loaded": dataAttr(isImgLoaded),
className: slots.img({ class: classNames == null ? void 0 : classNames.img }),
...imgProps
}
),
fallback
]
}
)
}
),
/* @__PURE__ */ jsx(
Tooltip,
{
className: "shadow-medium pointer-events-none mt-1 rounded bg-neutral-900 text-neutral-50 dark:bg-neutral-900 dark:text-neutral-50",
placement: "bottom",
children: /* @__PURE__ */ jsx("p", { className: "px-2 py-1 text-[10px]", children: alt })
}
)
] });
}
var _Avatar = forwardRef(Avatar);
export {
AvatarContext,
_Avatar
};