|
| 1 | +import React, { cloneElement } from "react"; |
| 2 | +import { AvatarProps } from "./Avatar.types"; |
| 3 | +import "./Avatar.scss"; |
| 4 | + |
| 5 | +// Parse the initials of the user from their name |
| 6 | +const parseInitials = (name: string) => { |
| 7 | + if (!name) return "✷"; // If no name then return "✷" |
| 8 | + if (name.length <= 2) return name; // If name is 2 characters or less then return it whole |
| 9 | + if (name.substring(0, 2).toLowerCase() === "0x") name = name.substring(2); // If starts with "0x" then remove it |
| 10 | + let nameArray = name.split(" "); // Initials by 'space' separator |
| 11 | + if (nameArray.length <= 1) nameArray = name.split("."); // Initials by 'dot' separator |
| 12 | + if (nameArray.length <= 1) nameArray = name.split("_"); // Initials by 'underscore' separator |
| 13 | + if (nameArray.length <= 1) nameArray = name.split("-"); // Initials by 'dash' separator |
| 14 | + if (nameArray.length <= 1) { |
| 15 | + const nameString = name.replace(/([A-Z])/g, " $1").trim(); // Initials by case-change |
| 16 | + nameArray = nameString.split(" "); |
| 17 | + } |
| 18 | + if (nameArray.length <= 1) return name.substring(0, 1); // Fallback to first letter of name |
| 19 | + return `${nameArray[0].substring(0, 1)}${nameArray[1].substring(0, 1)}`; |
| 20 | +}; |
| 21 | + |
| 22 | +// Array of dark pastel colors suitable as bg for the white initials |
| 23 | +const pastelColors = [ |
| 24 | + "#9B4DCA", |
| 25 | + "#8E44AD", |
| 26 | + "#2980B9", |
| 27 | + "#3498DB", |
| 28 | + "#1ABC9C", |
| 29 | + "#16A085", |
| 30 | + "#27AE60", |
| 31 | + "#2ECC71", |
| 32 | + "#F1C40F", |
| 33 | + "#F39C12", |
| 34 | + "#E67E22", |
| 35 | + "#D35400", |
| 36 | + "#E74C3C", |
| 37 | + "#C0392B", |
| 38 | + "#EC407A", |
| 39 | + "#D81B60", |
| 40 | + "#8E24AA", |
| 41 | + "#6A1B9A", |
| 42 | + "#4A148C", |
| 43 | + "#4527A0", |
| 44 | +]; |
| 45 | + |
| 46 | +// Generate a random color from the name string |
| 47 | +const generateColor = (str: string) => { |
| 48 | + const index = str.length % pastelColors.length; |
| 49 | + return pastelColors[index]; |
| 50 | +}; |
| 51 | + |
| 52 | +/** |
| 53 | + * A stylized Avatar component for displaying user avatars. |
| 54 | + * This component supports displaying an image avatar or a fallback avatar with initials. |
| 55 | + * The fallback avatar is a colored circle with the user's initials. |
| 56 | + * |
| 57 | + * @param imgElement - An optional image element to use as the avatar. |
| 58 | + * @param name - The name of the user. Used to generate initials for the fallback avatar. |
| 59 | + * @param size - The size of the avatar in pixels. |
| 60 | + * @param round - The border-radius of the avatar in pixels. Use this to make the avatar round. |
| 61 | + */ |
| 62 | +export const Avatar: React.FC<AvatarProps> = ({ |
| 63 | + imgElement, |
| 64 | + name, |
| 65 | + size, |
| 66 | + round, |
| 67 | +}) => { |
| 68 | + const clonedImgElement = imgElement |
| 69 | + ? cloneElement(imgElement, { |
| 70 | + className: "avatar__image", |
| 71 | + width: size, |
| 72 | + height: size, |
| 73 | + }) |
| 74 | + : null; |
| 75 | + |
| 76 | + return ( |
| 77 | + <div |
| 78 | + className={"avatar"} |
| 79 | + style={{ |
| 80 | + borderRadius: round ? `${round}px` : "none", |
| 81 | + width: `${size}px`, |
| 82 | + height: `${size}px`, |
| 83 | + }} |
| 84 | + > |
| 85 | + {clonedImgElement ? ( |
| 86 | + clonedImgElement |
| 87 | + ) : ( |
| 88 | + <div |
| 89 | + className={"avatar__initials"} |
| 90 | + style={{ |
| 91 | + width: `${size}px`, |
| 92 | + height: `${size}px`, |
| 93 | + backgroundColor: generateColor(name), |
| 94 | + }} |
| 95 | + > |
| 96 | + <span |
| 97 | + className={"avatar__initials-text"} |
| 98 | + style={{ |
| 99 | + lineHeight: `${size}px`, |
| 100 | + fontSize: `${size * 0.45}px`, |
| 101 | + }} |
| 102 | + > |
| 103 | + {parseInitials(name)} |
| 104 | + </span> |
| 105 | + </div> |
| 106 | + )} |
| 107 | + </div> |
| 108 | + ); |
| 109 | +}; |
0 commit comments