feat: avatar component
This commit is contained in:
parent
c8f1c58507
commit
49b1602ab5
@ -6,6 +6,7 @@
|
|||||||
"@fontsource/inter": "^5.0.16",
|
"@fontsource/inter": "^5.0.16",
|
||||||
"@material-tailwind/react": "^2.1.7",
|
"@material-tailwind/react": "^2.1.7",
|
||||||
"@radix-ui/react-checkbox": "^1.0.4",
|
"@radix-ui/react-checkbox": "^1.0.4",
|
||||||
|
"@radix-ui/react-avatar": "^1.0.4",
|
||||||
"@testing-library/jest-dom": "^5.17.0",
|
"@testing-library/jest-dom": "^5.17.0",
|
||||||
"@testing-library/react": "^13.4.0",
|
"@testing-library/react": "^13.4.0",
|
||||||
"@testing-library/user-event": "^13.5.0",
|
"@testing-library/user-event": "^13.5.0",
|
||||||
|
@ -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<typeof avatarTheme>;
|
40
packages/frontend/src/components/shared/Avatar/Avatar.tsx
Normal file
40
packages/frontend/src/components/shared/Avatar/Avatar.tsx
Normal file
@ -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<typeof PrimitiveAvatar.Image>;
|
||||||
|
fallbackProps?: ComponentProps<typeof PrimitiveAvatar.Fallback>;
|
||||||
|
} & AvatarVariants;
|
||||||
|
|
||||||
|
export const Avatar = ({
|
||||||
|
className,
|
||||||
|
size,
|
||||||
|
type,
|
||||||
|
imageSrc,
|
||||||
|
imageProps,
|
||||||
|
fallbackProps,
|
||||||
|
initials,
|
||||||
|
}: AvatarProps) => {
|
||||||
|
const { base, image, fallback } = avatarTheme({ size, type });
|
||||||
|
|
||||||
|
return (
|
||||||
|
<PrimitiveAvatar.Root className={base({ className })}>
|
||||||
|
{imageSrc && (
|
||||||
|
<PrimitiveAvatar.Image
|
||||||
|
{...imageProps}
|
||||||
|
className={image({ className: imageProps?.className })}
|
||||||
|
src={imageSrc}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
<PrimitiveAvatar.Fallback asChild {...fallbackProps}>
|
||||||
|
<div className={fallback({ className: fallbackProps?.className })}>
|
||||||
|
{initials}
|
||||||
|
</div>
|
||||||
|
</PrimitiveAvatar.Fallback>
|
||||||
|
</PrimitiveAvatar.Root>
|
||||||
|
);
|
||||||
|
};
|
2
packages/frontend/src/components/shared/Avatar/index.ts
Normal file
2
packages/frontend/src/components/shared/Avatar/index.ts
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
export * from './Avatar';
|
||||||
|
export * from './Avatar.theme';
|
@ -1,3 +1,4 @@
|
|||||||
|
import { Avatar, AvatarVariants } from 'components/shared/Avatar';
|
||||||
import { Badge, BadgeProps } from 'components/shared/Badge';
|
import { Badge, BadgeProps } from 'components/shared/Badge';
|
||||||
import { Button, ButtonOrLinkProps } from 'components/shared/Button';
|
import { Button, ButtonOrLinkProps } from 'components/shared/Button';
|
||||||
import { Calendar } from 'components/shared/Calendar';
|
import { Calendar } from 'components/shared/Calendar';
|
||||||
@ -6,10 +7,37 @@ import { PlusIcon } from 'components/shared/CustomIcon';
|
|||||||
import React, { useState } from 'react';
|
import React, { useState } from 'react';
|
||||||
import { Value } from 'react-calendar/dist/cjs/shared/types';
|
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 Page = () => {
|
||||||
const [singleDate, setSingleDate] = useState<Value>();
|
const [singleDate, setSingleDate] = useState<Value>();
|
||||||
const [dateRange, setDateRange] = useState<Value>();
|
const [dateRange, setDateRange] = useState<Value>();
|
||||||
|
|
||||||
|
const avatars = avatarSizes.map((size) => {
|
||||||
|
return (
|
||||||
|
<Avatar
|
||||||
|
initials="SY"
|
||||||
|
key={String(size)}
|
||||||
|
size={size}
|
||||||
|
imageSrc="/gray.png"
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
const avatarsFallback = avatarVariants.map((color) => {
|
||||||
|
return avatarSizes.map((size) => {
|
||||||
|
return (
|
||||||
|
<Avatar
|
||||||
|
initials="SY"
|
||||||
|
key={`${color}-${size}`}
|
||||||
|
type={color}
|
||||||
|
size={size}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="relative h-full min-h-full">
|
<div className="relative h-full min-h-full">
|
||||||
<div className="flex flex-col items-center justify-center max-w-7xl mx-auto px-20 py-20">
|
<div className="flex flex-col items-center justify-center max-w-7xl mx-auto px-20 py-20">
|
||||||
@ -76,7 +104,6 @@ const Page = () => {
|
|||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="w-full h border border-gray-200 px-20 my-10" />
|
<div className="w-full h border border-gray-200 px-20 my-10" />
|
||||||
|
|
||||||
@ -151,7 +178,23 @@ const Page = () => {
|
|||||||
End date:{' '}
|
End date:{' '}
|
||||||
{dateRange instanceof Array ? dateRange[1]?.toString() : ''}
|
{dateRange instanceof Array ? dateRange[1]?.toString() : ''}
|
||||||
</p>
|
</p>
|
||||||
<Calendar selectRange value={dateRange} onChange={setDateRange} />
|
<Calendar
|
||||||
|
selectRange
|
||||||
|
value={dateRange}
|
||||||
|
onChange={setDateRange}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="w-full h border border-gray-200 px-20 my-10" />
|
||||||
|
|
||||||
|
{/* Avatar */}
|
||||||
|
<div className="flex flex-col gap-10 items-center justify-between">
|
||||||
|
<h1 className="text-2xl font-bold">Avatar</h1>
|
||||||
|
<div className="flex gap-10 flex-wrap max-w-[522px]">
|
||||||
|
{avatars}
|
||||||
|
{avatarsFallback}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
5
settings.json
Normal file
5
settings.json
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"tailwindCSS.experimental.classRegex": [
|
||||||
|
["tv\\((([^()]*|\\([^()]*\\))*)\\)", "[\"'`]([^\"'`]*).*?[\"'`]"]
|
||||||
|
]
|
||||||
|
}
|
57
yarn.lock
57
yarn.lock
@ -1280,7 +1280,7 @@
|
|||||||
resolved "https://registry.npmjs.org/@babel/regjsgen/-/regjsgen-0.8.0.tgz"
|
resolved "https://registry.npmjs.org/@babel/regjsgen/-/regjsgen-0.8.0.tgz"
|
||||||
integrity sha512-x/rqGMdzj+fWZvCOYForTghzbtqPDZ5gPwaoNGHdgDfF2QA/XZbCBp4Moo5scrkAMPhB7z26XM/AaHuIJdgauA==
|
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"
|
version "7.23.9"
|
||||||
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.23.9.tgz#47791a15e4603bb5f905bc0753801cf21d6345f7"
|
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.23.9.tgz#47791a15e4603bb5f905bc0753801cf21d6345f7"
|
||||||
integrity sha512-0CX6F+BI2s9dkUqr08KFrAIZgNFj75rdBU/DjCyYLIaV/quFjkk6T+EJ2LkZHyZTbEV4L5p97mNkUsHl2wLFAw==
|
integrity sha512-0CX6F+BI2s9dkUqr08KFrAIZgNFj75rdBU/DjCyYLIaV/quFjkk6T+EJ2LkZHyZTbEV4L5p97mNkUsHl2wLFAw==
|
||||||
@ -3277,6 +3277,61 @@
|
|||||||
resolved "https://registry.yarnpkg.com/@protobufjs/utf8/-/utf8-1.1.0.tgz#a777360b5b39a1a2e5106f8e858f2fd2d060c570"
|
resolved "https://registry.yarnpkg.com/@protobufjs/utf8/-/utf8-1.1.0.tgz#a777360b5b39a1a2e5106f8e858f2fd2d060c570"
|
||||||
integrity sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==
|
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":
|
"@radix-ui/primitive@1.0.1":
|
||||||
version "1.0.1"
|
version "1.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/@radix-ui/primitive/-/primitive-1.0.1.tgz#e46f9958b35d10e9f6dc71c497305c22e3e55dbd"
|
resolved "https://registry.yarnpkg.com/@radix-ui/primitive/-/primitive-1.0.1.tgz#e46f9958b35d10e9f6dc71c497305c22e3e55dbd"
|
||||||
|
Loading…
Reference in New Issue
Block a user