From 49b1602ab533ab278308521bc18023d53a6d4ebd Mon Sep 17 00:00:00 2001 From: Zachery Ng Date: Mon, 19 Feb 2024 20:51:08 +0800 Subject: [PATCH] feat: avatar component --- packages/frontend/package.json | 1 + .../components/shared/Avatar/Avatar.theme.ts | 71 +++++++ .../src/components/shared/Avatar/Avatar.tsx | 40 ++++ .../src/components/shared/Avatar/index.ts | 2 + .../frontend/src/pages/components/index.tsx | 189 +++++++++++------- settings.json | 5 + yarn.lock | 57 +++++- 7 files changed, 291 insertions(+), 74 deletions(-) create mode 100644 packages/frontend/src/components/shared/Avatar/Avatar.theme.ts create mode 100644 packages/frontend/src/components/shared/Avatar/Avatar.tsx create mode 100644 packages/frontend/src/components/shared/Avatar/index.ts create mode 100644 settings.json diff --git a/packages/frontend/package.json b/packages/frontend/package.json index d1e6e78b..7e369024 100644 --- a/packages/frontend/package.json +++ b/packages/frontend/package.json @@ -6,6 +6,7 @@ "@fontsource/inter": "^5.0.16", "@material-tailwind/react": "^2.1.7", "@radix-ui/react-checkbox": "^1.0.4", + "@radix-ui/react-avatar": "^1.0.4", "@testing-library/jest-dom": "^5.17.0", "@testing-library/react": "^13.4.0", "@testing-library/user-event": "^13.5.0", diff --git a/packages/frontend/src/components/shared/Avatar/Avatar.theme.ts b/packages/frontend/src/components/shared/Avatar/Avatar.theme.ts new file mode 100644 index 00000000..a049cbeb --- /dev/null +++ b/packages/frontend/src/components/shared/Avatar/Avatar.theme.ts @@ -0,0 +1,71 @@ +import { tv, type VariantProps } from 'tailwind-variants'; + +export const avatarTheme = tv( + { + base: ['relative', 'block', 'rounded-full', 'overflow-hidden'], + slots: { + image: [ + 'h-full', + 'w-full', + 'rounded-[inherit]', + 'object-cover', + 'object-center', + ], + fallback: [ + 'grid', + 'select-none', + 'place-content-center', + 'h-full', + 'w-full', + 'rounded-[inherit]', + 'font-medium', + ], + }, + variants: { + type: { + gray: { + fallback: ['text-elements-highEm', 'bg-base-bg-emphasized'], + }, + orange: { + fallback: ['text-elements-warning', 'bg-base-bg-emphasized-warning'], + }, + blue: { + fallback: ['text-elements-info', 'bg-base-bg-emphasized-info'], + }, + }, + size: { + 18: { + base: ['rounded-[6px]', 'h-[18px]', 'w-[18px]', 'text-[0.625rem]'], + }, + 20: { + base: ['rounded-[6px]', 'h-[20px]', 'w-[20px]', 'text-[0.625rem]'], + }, + 24: { + base: ['rounded-[6px]', 'h-[24px]', 'w-[24px]', 'text-[0.625rem]'], + }, + 28: { + base: ['rounded-[8px]', 'h-[28px]', 'w-[28px]', 'text-[0.625rem]'], + }, + 32: { + base: ['rounded-[8px]', 'h-[32px]', 'w-[32px]', 'text-xs'], + }, + 36: { + base: ['rounded-[12px]', 'h-[36px]', 'w-[36px]', 'text-xs'], + }, + 40: { + base: ['rounded-[12px]', 'h-[40px]', 'w-[40px]', 'text-sm'], + }, + 44: { + base: ['rounded-[12px]', 'h-[44px]', 'w-[44px]', 'text-sm'], + }, + }, + }, + defaultVariants: { + size: 24, + type: 'gray', + }, + }, + { responsiveVariants: true }, +); + +export type AvatarVariants = VariantProps; diff --git a/packages/frontend/src/components/shared/Avatar/Avatar.tsx b/packages/frontend/src/components/shared/Avatar/Avatar.tsx new file mode 100644 index 00000000..fd7b1e59 --- /dev/null +++ b/packages/frontend/src/components/shared/Avatar/Avatar.tsx @@ -0,0 +1,40 @@ +import React from 'react'; +import { type ComponentPropsWithoutRef, type ComponentProps } from 'react'; +import { avatarTheme, type AvatarVariants } from './Avatar.theme'; +import * as PrimitiveAvatar from '@radix-ui/react-avatar'; + +export type AvatarProps = ComponentPropsWithoutRef<'div'> & { + imageSrc?: string | null; + initials?: string; + imageProps?: ComponentProps; + fallbackProps?: ComponentProps; +} & AvatarVariants; + +export const Avatar = ({ + className, + size, + type, + imageSrc, + imageProps, + fallbackProps, + initials, +}: AvatarProps) => { + const { base, image, fallback } = avatarTheme({ size, type }); + + return ( + + {imageSrc && ( + + )} + +
+ {initials} +
+
+
+ ); +}; diff --git a/packages/frontend/src/components/shared/Avatar/index.ts b/packages/frontend/src/components/shared/Avatar/index.ts new file mode 100644 index 00000000..35478c8f --- /dev/null +++ b/packages/frontend/src/components/shared/Avatar/index.ts @@ -0,0 +1,2 @@ +export * from './Avatar'; +export * from './Avatar.theme'; diff --git a/packages/frontend/src/pages/components/index.tsx b/packages/frontend/src/pages/components/index.tsx index 74086848..456f433e 100644 --- a/packages/frontend/src/pages/components/index.tsx +++ b/packages/frontend/src/pages/components/index.tsx @@ -1,3 +1,4 @@ +import { Avatar, AvatarVariants } from 'components/shared/Avatar'; import { Badge, BadgeProps } from 'components/shared/Badge'; import { Button, ButtonOrLinkProps } from 'components/shared/Button'; import { Calendar } from 'components/shared/Calendar'; @@ -6,10 +7,37 @@ import { PlusIcon } from 'components/shared/CustomIcon'; import React, { useState } from 'react'; import { Value } from 'react-calendar/dist/cjs/shared/types'; +const avatarSizes: AvatarVariants['size'][] = [18, 20, 24, 28, 32, 36, 40, 44]; +const avatarVariants: AvatarVariants['type'][] = ['gray', 'orange', 'blue']; + const Page = () => { const [singleDate, setSingleDate] = useState(); const [dateRange, setDateRange] = useState(); + const avatars = avatarSizes.map((size) => { + return ( + + ); + }); + + const avatarsFallback = avatarVariants.map((color) => { + return avatarSizes.map((size) => { + return ( + + ); + }); + }); + return (
@@ -76,82 +104,97 @@ const Page = () => {
))}
- -
+
-
-

Badge

-
- {['primary', 'secondary', 'tertiary', 'inset'].map( - (variant, index) => ( -
- {['sm', 'xs'].map((size) => ( - - 1 - - ))} -
- ), - )} -
-
- -
- -
-

Checkbox

-
- {Array.from({ length: 5 }).map((_, index) => ( - - ))} -
-
- {Array.from({ length: 2 }).map((_, index) => ( - - ))} -
-
- -
- -
-

Calendar

-
-
-

Selected date: {singleDate?.toString()}

- +
+

Badge

+
+ {['primary', 'secondary', 'tertiary', 'inset'].map( + (variant, index) => ( +
+ {['sm', 'xs'].map((size) => ( + + 1 + + ))} +
+ ), + )}
-
-

- Start date:{' '} - {dateRange instanceof Array ? dateRange[0]?.toString() : ''}{' '} -
- End date:{' '} - {dateRange instanceof Array ? dateRange[1]?.toString() : ''} -

- +
+ +
+ +
+

Checkbox

+
+ {Array.from({ length: 5 }).map((_, index) => ( + + ))} +
+
+ {Array.from({ length: 2 }).map((_, index) => ( + + ))} +
+
+ +
+ +
+

Calendar

+
+
+

Selected date: {singleDate?.toString()}

+ +
+
+

+ Start date:{' '} + {dateRange instanceof Array ? dateRange[0]?.toString() : ''}{' '} +
+ End date:{' '} + {dateRange instanceof Array ? dateRange[1]?.toString() : ''} +

+ +
+
+ +
+ + {/* Avatar */} +
+

Avatar

+
+ {avatars} + {avatarsFallback} +
diff --git a/settings.json b/settings.json new file mode 100644 index 00000000..846b0589 --- /dev/null +++ b/settings.json @@ -0,0 +1,5 @@ +{ + "tailwindCSS.experimental.classRegex": [ + ["tv\\((([^()]*|\\([^()]*\\))*)\\)", "[\"'`]([^\"'`]*).*?[\"'`]"] + ] +} diff --git a/yarn.lock b/yarn.lock index 42d230b9..87b34483 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1280,7 +1280,7 @@ resolved "https://registry.npmjs.org/@babel/regjsgen/-/regjsgen-0.8.0.tgz" integrity sha512-x/rqGMdzj+fWZvCOYForTghzbtqPDZ5gPwaoNGHdgDfF2QA/XZbCBp4Moo5scrkAMPhB7z26XM/AaHuIJdgauA== -"@babel/runtime@^7.10.4", "@babel/runtime@^7.13.10", "@babel/runtime@^7.23.7", "@babel/runtime@^7.3.1": +"@babel/runtime@^7.10.4", "@babel/runtime@^7.13.10", "@babel/runtime@^7.23.7", "@babel/runtime@^7.13.10", "@babel/runtime@^7.23.7", "@babel/runtime@^7.3.1": version "7.23.9" resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.23.9.tgz#47791a15e4603bb5f905bc0753801cf21d6345f7" integrity sha512-0CX6F+BI2s9dkUqr08KFrAIZgNFj75rdBU/DjCyYLIaV/quFjkk6T+EJ2LkZHyZTbEV4L5p97mNkUsHl2wLFAw== @@ -3277,6 +3277,61 @@ resolved "https://registry.yarnpkg.com/@protobufjs/utf8/-/utf8-1.1.0.tgz#a777360b5b39a1a2e5106f8e858f2fd2d060c570" integrity sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw== +"@radix-ui/react-avatar@^1.0.4": + version "1.0.4" + resolved "https://registry.yarnpkg.com/@radix-ui/react-avatar/-/react-avatar-1.0.4.tgz#de9a5349d9e3de7bbe990334c4d2011acbbb9623" + integrity sha512-kVK2K7ZD3wwj3qhle0ElXhOjbezIgyl2hVvgwfIdexL3rN6zJmy5AqqIf+D31lxVppdzV8CjAfZ6PklkmInZLw== + dependencies: + "@babel/runtime" "^7.13.10" + "@radix-ui/react-context" "1.0.1" + "@radix-ui/react-primitive" "1.0.3" + "@radix-ui/react-use-callback-ref" "1.0.1" + "@radix-ui/react-use-layout-effect" "1.0.1" + +"@radix-ui/react-compose-refs@1.0.1": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@radix-ui/react-compose-refs/-/react-compose-refs-1.0.1.tgz#7ed868b66946aa6030e580b1ffca386dd4d21989" + integrity sha512-fDSBgd44FKHa1FRMU59qBMPFcl2PZE+2nmqunj+BWFyYYjnhIDWL2ItDs3rrbJDQOtzt5nIebLCQc4QRfz6LJw== + dependencies: + "@babel/runtime" "^7.13.10" + +"@radix-ui/react-context@1.0.1": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@radix-ui/react-context/-/react-context-1.0.1.tgz#fe46e67c96b240de59187dcb7a1a50ce3e2ec00c" + integrity sha512-ebbrdFoYTcuZ0v4wG5tedGnp9tzcV8awzsxYph7gXUyvnNLuTIcCk1q17JEbnVhXAKG9oX3KtchwiMIAYp9NLg== + dependencies: + "@babel/runtime" "^7.13.10" + +"@radix-ui/react-primitive@1.0.3": + version "1.0.3" + resolved "https://registry.yarnpkg.com/@radix-ui/react-primitive/-/react-primitive-1.0.3.tgz#d49ea0f3f0b2fe3ab1cb5667eb03e8b843b914d0" + integrity sha512-yi58uVyoAcK/Nq1inRY56ZSjKypBNKTa/1mcL8qdl6oJeEaDbOldlzrGn7P6Q3Id5d+SYNGc5AJgc4vGhjs5+g== + dependencies: + "@babel/runtime" "^7.13.10" + "@radix-ui/react-slot" "1.0.2" + +"@radix-ui/react-slot@1.0.2": + version "1.0.2" + resolved "https://registry.yarnpkg.com/@radix-ui/react-slot/-/react-slot-1.0.2.tgz#a9ff4423eade67f501ffb32ec22064bc9d3099ab" + integrity sha512-YeTpuq4deV+6DusvVUW4ivBgnkHwECUu0BiN43L5UCDFgdhsRUWAghhTF5MbvNTPzmiFOx90asDSUjWuCNapwg== + dependencies: + "@babel/runtime" "^7.13.10" + "@radix-ui/react-compose-refs" "1.0.1" + +"@radix-ui/react-use-callback-ref@1.0.1": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.0.1.tgz#f4bb1f27f2023c984e6534317ebc411fc181107a" + integrity sha512-D94LjX4Sp0xJFVaoQOd3OO9k7tpBYNOXdVhkltUbGv2Qb9OXdrg/CpsjlZv7ia14Sylv398LswWBVVu5nqKzAQ== + dependencies: + "@babel/runtime" "^7.13.10" + +"@radix-ui/react-use-layout-effect@1.0.1": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.0.1.tgz#be8c7bc809b0c8934acf6657b577daf948a75399" + integrity sha512-v/5RegiJWYdoCvMnITBkNNx6bCj20fiaJnWtRkU18yITptraXjffz5Qbn05uOiQnOvi+dbkznkoaMltz1GnszQ== + dependencies: + "@babel/runtime" "^7.13.10" + "@radix-ui/primitive@1.0.1": version "1.0.1" resolved "https://registry.yarnpkg.com/@radix-ui/primitive/-/primitive-1.0.1.tgz#e46f9958b35d10e9f6dc71c497305c22e3e55dbd"