From 646cf3510837b63babdc5fe35b9c07c3fa7b9fc7 Mon Sep 17 00:00:00 2001 From: Dani Moreno <96433370+dani-moreno@users.noreply.github.com> Date: Thu, 19 Dec 2024 16:32:07 +0100 Subject: [PATCH] Badge in avatars (#988) * Display Badge in avatars * avatar size * update badge --- .../Information/Avatars/BaseAvatar/index.tsx | 63 +++++++++++++------ .../Information/Avatars/BaseAvatar/utils.ts | 38 ++++++++++- .../Avatars/CompanyAvatar/index.stories.tsx | 11 ++++ .../Avatars/CompanyAvatar/index.tsx | 4 ++ .../Avatars/PersonAvatar/index.stories.tsx | 10 +++ .../Avatars/PersonAvatar/index.tsx | 4 ++ .../Avatars/TeamAvatar/index.stories.tsx | 10 +++ .../Information/Avatars/TeamAvatar/index.tsx | 4 ++ lib/experimental/Information/Badge/index.tsx | 4 +- .../Celebration/components/avatar.tsx | 4 +- lib/ui/avatar.tsx | 5 +- 11 files changed, 132 insertions(+), 25 deletions(-) diff --git a/lib/experimental/Information/Avatars/BaseAvatar/index.tsx b/lib/experimental/Information/Avatars/BaseAvatar/index.tsx index 446f9d76f..92db4ce99 100644 --- a/lib/experimental/Information/Avatars/BaseAvatar/index.tsx +++ b/lib/experimental/Information/Avatars/BaseAvatar/index.tsx @@ -1,10 +1,17 @@ +import { Badge, BadgeProps } from "@/experimental/Information/Badge" import { Avatar as AvatarComponent, AvatarFallback, AvatarImage, } from "@/ui/avatar" import { ComponentProps, forwardRef } from "react" -import { getAvatarColor, getInitials } from "./utils" +import { getAvatarColor, getInitials, getMask } from "./utils" + +const getBadgeSize = (size: ShadAvatarProps["size"]) => { + if (size === "xlarge") return "lg" + if (size === "large") return "md" + return "sm" +} type ShadAvatarProps = ComponentProps @@ -14,6 +21,7 @@ type Props = { src?: string size?: ShadAvatarProps["size"] color?: ShadAvatarProps["color"] | "random" + badge?: BadgeProps } & Pick export const BaseAvatar = forwardRef( @@ -22,10 +30,11 @@ export const BaseAvatar = forwardRef( src, name, size, - type, + type = "base", color = "random", "aria-label": ariaLabel, "aria-labelledby": ariaLabelledby, + badge, }, ref ) => { @@ -38,22 +47,40 @@ export const BaseAvatar = forwardRef( const hasAria = Boolean(ariaLabel || ariaLabelledby) return ( - - - {initials} - +
+
+ + + {initials} + +
+ {badge && ( +
+ +
+ )} +
) } ) diff --git a/lib/experimental/Information/Avatars/BaseAvatar/utils.ts b/lib/experimental/Information/Avatars/BaseAvatar/utils.ts index a5715b8c5..4ed382802 100644 --- a/lib/experimental/Information/Avatars/BaseAvatar/utils.ts +++ b/lib/experimental/Information/Avatars/BaseAvatar/utils.ts @@ -1,4 +1,9 @@ -import { color as AvatarColors, Avatar as AvatarComponent } from "@/ui/avatar" +import { + color as AvatarColors, + Avatar as AvatarComponent, + type as avatarType, + sizes, +} from "@/ui/avatar" import { type ComponentProps } from "react" type ShadAvatarProps = ComponentProps @@ -33,3 +38,34 @@ export function getAvatarColor(text: string): ShadAvatarProps["color"] { return AvatarColors[index] } + +export const getMask = { + base: { + xlarge: + "path('M72 0H0V72H52.202C49.6089 69.459 48 65.9174 48 62C48 54.268 54.268 48 62 48C65.9174 48 69.459 49.6089 72 52.202V0ZM72 71.798C71.9333 71.866 71.866 71.9333 71.798 72H72V71.798Z')", + large: + "path('M56 0H0V56H39.0556C37.1554 53.877 36 51.0734 36 48C36 41.3726 41.3726 36 48 36C51.0734 36 53.877 37.1554 56 39.0556V0Z')", + medium: + "path('M32 0H0V32H22.2547C21.4638 30.8662 21 29.4872 21 28C21 24.134 24.134 21 28 21C29.4872 21 30.8662 21.4638 32 22.2547V0Z')", + small: + "path('M24 0H0V24H14.2547C13.4638 22.8662 13 21.4872 13 20C13 16.134 16.134 13 20 13C21.4872 13 22.8662 13.4638 24 14.2547V0Z')", + xsmall: + "path('M20 0H0V20H10.2547C9.46381 18.8662 9 17.4872 9 16C9 12.134 12.134 9 16 9C17.4872 9 18.8662 9.46381 20 10.2547V0Z')", + }, + rounded: { + xlarge: + "path('M69.1842 49.9814C70.9975 45.6828 72 40.9585 72 36C72 16.1177 55.8823 0 36 0C16.1177 0 0 16.1177 0 36C0 55.8823 16.1177 72 36 72C40.9585 72 45.6828 70.9975 49.9814 69.1842C48.7232 67.0839 48 64.6264 48 62C48 54.268 54.268 48 62 48C64.6264 48 67.0839 48.7232 69.1842 49.9814Z')", + large: + "path('M54.2534 37.7562C55.3828 34.7182 56 31.4312 56 28C56 12.536 43.464 0 28 0C12.536 0 0 12.536 0 28C0 43.464 12.536 56 28 56C31.4312 56 34.7182 55.3828 37.7562 54.2534C36.6421 52.4324 36 50.2912 36 48C36 41.3726 41.3726 36 48 36C50.2912 36 52.4324 36.6421 54.2534 37.7562Z')", + medium: + "path('M30.9702 21.6596C31.6358 19.9001 32 17.9926 32 16C32 7.16344 24.8366 0 16 0C7.16344 0 0 7.16344 0 16C0 24.8366 7.16344 32 16 32C17.9926 32 19.9001 31.6358 21.6596 30.9702C21.2365 30.0686 21 29.0619 21 28C21 24.134 24.134 21 28 21C29.0619 21 30.0686 21.2365 30.9702 21.6596Z')", + small: + "path('M23.8119 14.128C23.9355 13.4373 24 12.7262 24 12C24 5.37258 18.6274 0 12 0C5.37258 0 0 5.37258 0 12C0 18.6274 5.37258 24 12 24C12.7262 24 13.4373 23.9355 14.128 23.8119C13.4145 22.7151 13 21.406 13 20C13 16.134 16.134 13 20 13C21.406 13 22.7151 13.4145 23.8119 14.128Z')", + xsmall: + "path('M19.9969 10.2525C19.999 10.1686 20 10.0844 20 10C20 4.47715 15.5228 0 10 0C4.47715 0 0 4.47715 0 10C0 15.5228 4.47715 20 10 20C10.0844 20 10.1686 19.999 10.2525 19.9969C9.46297 18.8636 9 17.4859 9 16C9 12.134 12.134 9 16 9C17.4859 9 18.8636 9.46297 19.9969 10.2525Z')", + }, + get: ( + type: (typeof avatarType)[number] = "base", + size: (typeof sizes)[number] = "medium" + ) => getMask[type][size], +} as const diff --git a/lib/experimental/Information/Avatars/CompanyAvatar/index.stories.tsx b/lib/experimental/Information/Avatars/CompanyAvatar/index.stories.tsx index 8f5e1c271..d237ff40c 100644 --- a/lib/experimental/Information/Avatars/CompanyAvatar/index.stories.tsx +++ b/lib/experimental/Information/Avatars/CompanyAvatar/index.stories.tsx @@ -1,3 +1,4 @@ +import { Check } from "@/icons/app" import { sizes } from "@/ui/avatar" import type { Meta, StoryObj } from "@storybook/react" import { CompanyAvatar } from "." @@ -41,3 +42,13 @@ export const WithImage: Story = { src: "https://avatars.githubusercontent.com/u/21041797?s=48&v=4", }, } + +export const WithBadge: Story = { + args: { + src: "https://avatars.githubusercontent.com/u/21041797?s=48&v=4", + badge: { + type: "positive", + icon: Check, + }, + }, +} diff --git a/lib/experimental/Information/Avatars/CompanyAvatar/index.tsx b/lib/experimental/Information/Avatars/CompanyAvatar/index.tsx index d6bf97b3c..62a93544b 100644 --- a/lib/experimental/Information/Avatars/CompanyAvatar/index.tsx +++ b/lib/experimental/Information/Avatars/CompanyAvatar/index.tsx @@ -1,3 +1,4 @@ +import { BadgeProps } from "@/experimental/Information/Badge" import { ComponentProps } from "react" import { BaseAvatar } from "../BaseAvatar" @@ -7,6 +8,7 @@ type Props = { name: string src?: string size?: BaseAvatarProps["size"] + badge?: BadgeProps } & Pick export const CompanyAvatar = ({ @@ -15,6 +17,7 @@ export const CompanyAvatar = ({ size, "aria-label": ariaLabel, "aria-labelledby": ariaLabelledby, + badge, }: Props) => { return ( ) } diff --git a/lib/experimental/Information/Avatars/PersonAvatar/index.stories.tsx b/lib/experimental/Information/Avatars/PersonAvatar/index.stories.tsx index 163aad29a..fc8c3b714 100644 --- a/lib/experimental/Information/Avatars/PersonAvatar/index.stories.tsx +++ b/lib/experimental/Information/Avatars/PersonAvatar/index.stories.tsx @@ -1,3 +1,4 @@ +import { Check } from "@/icons/app" import { sizes } from "@/ui/avatar" import type { Meta, StoryObj } from "@storybook/react" import { PersonAvatar } from "." @@ -41,3 +42,12 @@ export const WithImage: Story = { src: "https://github.com/dani-moreno.png", }, } + +export const WithBadge: Story = { + args: { + badge: { + type: "positive", + icon: Check, + }, + }, +} diff --git a/lib/experimental/Information/Avatars/PersonAvatar/index.tsx b/lib/experimental/Information/Avatars/PersonAvatar/index.tsx index 563613969..aae439055 100644 --- a/lib/experimental/Information/Avatars/PersonAvatar/index.tsx +++ b/lib/experimental/Information/Avatars/PersonAvatar/index.tsx @@ -1,3 +1,4 @@ +import { BadgeProps } from "@/experimental/Information/Badge" import { ComponentProps } from "react" import { BaseAvatar } from "../BaseAvatar" @@ -8,6 +9,7 @@ type Props = { lastName: string src?: string size?: BaseAvatarProps["size"] + badge?: BadgeProps } & Pick export const PersonAvatar = ({ @@ -17,6 +19,7 @@ export const PersonAvatar = ({ size, "aria-label": ariaLabel, "aria-labelledby": ariaLabelledby, + badge, }: Props) => { return ( ) } diff --git a/lib/experimental/Information/Avatars/TeamAvatar/index.stories.tsx b/lib/experimental/Information/Avatars/TeamAvatar/index.stories.tsx index ed46c1e19..a353abd2c 100644 --- a/lib/experimental/Information/Avatars/TeamAvatar/index.stories.tsx +++ b/lib/experimental/Information/Avatars/TeamAvatar/index.stories.tsx @@ -1,3 +1,4 @@ +import { Check } from "@/icons/app" import { sizes } from "@/ui/avatar" import type { Meta, StoryObj } from "@storybook/react" import { TeamAvatar } from "." @@ -40,3 +41,12 @@ export const WithImage: Story = { src: "https://avatars.githubusercontent.com/u/21041797?s=48&v=4", }, } + +export const WithBadge: Story = { + args: { + badge: { + type: "positive", + icon: Check, + }, + }, +} diff --git a/lib/experimental/Information/Avatars/TeamAvatar/index.tsx b/lib/experimental/Information/Avatars/TeamAvatar/index.tsx index b5b9f73f3..57bce0f91 100644 --- a/lib/experimental/Information/Avatars/TeamAvatar/index.tsx +++ b/lib/experimental/Information/Avatars/TeamAvatar/index.tsx @@ -1,3 +1,4 @@ +import { BadgeProps } from "@/experimental/Information/Badge" import { ComponentProps } from "react" import { BaseAvatar } from "../BaseAvatar" @@ -7,6 +8,7 @@ type Props = { name: string src?: string size?: BaseAvatarProps["size"] + badge?: BadgeProps } & Pick export const TeamAvatar = ({ @@ -15,6 +17,7 @@ export const TeamAvatar = ({ size, "aria-label": ariaLabel, "aria-labelledby": ariaLabelledby, + badge, }: Props) => { return ( ) } diff --git a/lib/experimental/Information/Badge/index.tsx b/lib/experimental/Information/Badge/index.tsx index 332a80e30..90d3c3f73 100644 --- a/lib/experimental/Information/Badge/index.tsx +++ b/lib/experimental/Information/Badge/index.tsx @@ -31,9 +31,9 @@ const iconSizes = { lg: "md", } as const -interface BadgeProps extends VariantProps { +export interface BadgeProps extends VariantProps { icon: IconType - size: keyof typeof iconSizes + size?: keyof typeof iconSizes } export const Badge = ({ type, size = "md", icon }: BadgeProps) => { diff --git a/lib/experimental/Information/Communities/Celebration/components/avatar.tsx b/lib/experimental/Information/Communities/Celebration/components/avatar.tsx index 4a711b3bf..825e600ba 100644 --- a/lib/experimental/Information/Communities/Celebration/components/avatar.tsx +++ b/lib/experimental/Information/Communities/Celebration/components/avatar.tsx @@ -53,11 +53,11 @@ export function CelebrationAvatar({ src={src} firstName={firstName} lastName={lastName} - size="large" + size="xlarge" /> {canReact && ( -
+