Files
plane/packages/propel/src/avatar/avatar.tsx
chuan bba4bb40c8
Some checks failed
CodeQL / Analyze (javascript) (push) Has been cancelled
CodeQL / Analyze (python) (push) Has been cancelled
feat: init
2025-11-11 01:56:44 +08:00

120 lines
3.3 KiB
TypeScript

import React from "react";
import { Avatar as AvatarPrimitive } from "@base-ui-components/react/avatar";
import { cn } from "../utils/classname";
export type TAvatarSize = "sm" | "md" | "base" | "lg" | number;
type Props = {
name?: string; //The name of the avatar which will be displayed on the tooltip
fallbackBackgroundColor?: string; //The background color if the avatar image fails to load
fallbackText?: string;
fallbackTextColor?: string; //The text color if the avatar image fails to load
showTooltip?: boolean;
size?: TAvatarSize; //The size of the avatars
shape?: "circle" | "square";
src?: string; //The source of the avatar image
className?: string;
};
/**
* Get the size details based on the size prop
* @param size The size of the avatar
* @returns The size details
*/
export const getSizeInfo = (size: TAvatarSize) => {
switch (size) {
case "sm":
return {
avatarSize: "h-4 w-4",
fontSize: "text-xs",
spacing: "-space-x-1",
};
case "md":
return {
avatarSize: "h-5 w-5",
fontSize: "text-xs",
spacing: "-space-x-1",
};
case "base":
return {
avatarSize: "h-6 w-6",
fontSize: "text-sm",
spacing: "-space-x-1.5",
};
case "lg":
return {
avatarSize: "h-7 w-7",
fontSize: "text-sm",
spacing: "-space-x-1.5",
};
default:
return {
avatarSize: "h-5 w-5",
fontSize: "text-xs",
spacing: "-space-x-1",
};
}
};
/**
* Get the border radius based on the shape prop
* @param shape The shape of the avatar
* @returns The border radius
*/
export const getBorderRadius = (shape: "circle" | "square") => {
switch (shape) {
case "circle":
return "rounded-full";
case "square":
return "rounded";
default:
return "rounded-full";
}
};
/**
* Check if the value is a valid number
* @param value The value to check
* @returns Whether the value is a valid number or not
*/
export const isAValidNumber = (value: unknown): value is number => typeof value === "number" && !Number.isNaN(value);
export const Avatar: React.FC<Props> = (props) => {
const {
name,
fallbackBackgroundColor,
fallbackText,
fallbackTextColor,
size = "md",
shape = "circle",
src,
className = "",
} = props;
// get size details based on the size prop
const sizeInfo = getSizeInfo(size);
const fallbackLetter = name?.[0]?.toUpperCase() ?? fallbackText ?? "?";
return (
<div
className={cn("grid place-items-center overflow-hidden", getBorderRadius(shape), {
[sizeInfo.avatarSize]: !isAValidNumber(size),
})}
tabIndex={-1}
>
<AvatarPrimitive.Root className={cn("h-full w-full", getBorderRadius(shape), className)}>
<AvatarPrimitive.Image src={src} width="48" height="48" />
<AvatarPrimitive.Fallback
className={cn(sizeInfo.fontSize, "grid h-full w-full place-items-center", getBorderRadius(shape), className)}
style={{
backgroundColor: fallbackBackgroundColor ?? "rgba(var(--color-primary-500))",
color: fallbackTextColor ?? "#ffffff",
}}
>
{fallbackLetter}
</AvatarPrimitive.Fallback>
</AvatarPrimitive.Root>
</div>
);
};