forked from cerc-io/snowballtools-base
feat: avatar component (#76)
This commit is contained in:
@@ -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",
|
||||
|
||||
@@ -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-md', 'h-[18px]', 'w-[18px]', 'text-[0.625rem]'],
|
||||
},
|
||||
20: {
|
||||
base: ['rounded-md', 'h-5', 'w-5', 'text-[0.625rem]'],
|
||||
},
|
||||
24: {
|
||||
base: ['rounded-md', 'h-6', 'w-6', 'text-[0.625rem]'],
|
||||
},
|
||||
28: {
|
||||
base: ['rounded-lg', 'h-[28px]', 'w-[28px]', 'text-[0.625rem]'],
|
||||
},
|
||||
32: {
|
||||
base: ['rounded-lg', 'h-8', 'w-8', 'text-xs'],
|
||||
},
|
||||
36: {
|
||||
base: ['rounded-xl', 'h-[36px]', 'w-[36px]', 'text-xs'],
|
||||
},
|
||||
40: {
|
||||
base: ['rounded-xl', 'h-10', 'w-10', 'text-sm'],
|
||||
},
|
||||
44: {
|
||||
base: ['rounded-xl', 'h-[44px]', 'w-[44px]', 'text-sm'],
|
||||
},
|
||||
},
|
||||
},
|
||||
defaultVariants: {
|
||||
size: 24,
|
||||
type: 'gray',
|
||||
},
|
||||
},
|
||||
{ responsiveVariants: true },
|
||||
);
|
||||
|
||||
export type AvatarVariants = VariantProps<typeof avatarTheme>;
|
||||
@@ -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>
|
||||
);
|
||||
};
|
||||
@@ -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 { 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<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 (
|
||||
<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">
|
||||
@@ -76,82 +104,97 @@ const Page = () => {
|
||||
</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" />
|
||||
|
||||
<div className="flex flex-col gap-10 items-center justify-between">
|
||||
<h1 className="text-2xl font-bold">Badge</h1>
|
||||
<div className="space-y-5">
|
||||
{['primary', 'secondary', 'tertiary', 'inset'].map(
|
||||
(variant, index) => (
|
||||
<div className="flex gap-5" key={index}>
|
||||
{['sm', 'xs'].map((size) => (
|
||||
<Badge
|
||||
key={size}
|
||||
variant={variant as BadgeProps['variant']}
|
||||
size={size as BadgeProps['size']}
|
||||
>
|
||||
1
|
||||
</Badge>
|
||||
))}
|
||||
</div>
|
||||
),
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="w-full h border border-gray-200 px-20 my-10" />
|
||||
|
||||
<div className="flex flex-col gap-10 items-center justify-between">
|
||||
<h1 className="text-2xl font-bold">Checkbox</h1>
|
||||
<div className="flex gap-10 flex-wrap">
|
||||
{Array.from({ length: 5 }).map((_, index) => (
|
||||
<Checkbox
|
||||
id={`checkbox-${index + 1}`}
|
||||
key={index}
|
||||
label={`Label ${index + 1}`}
|
||||
disabled={index === 2 || index === 4 ? true : false}
|
||||
checked={index === 4 ? true : undefined}
|
||||
value={`value-${index + 1}`}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
<div className="flex gap-10 flex-wrap">
|
||||
{Array.from({ length: 2 }).map((_, index) => (
|
||||
<Checkbox
|
||||
id={`checkbox-description-${index + 1}`}
|
||||
key={index}
|
||||
label={`Label ${index + 1}`}
|
||||
description={`Description of the checkbox ${index + 1}`}
|
||||
value={`value-with-description-${index + 1}`}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="w-full h border border-gray-200 px-20 my-10" />
|
||||
|
||||
<div className="flex flex-col gap-10 items-center justify-between">
|
||||
<h1 className="text-2xl font-bold">Calendar</h1>
|
||||
<div className="flex flex-col gap-10">
|
||||
<div className="space-y-5 flex flex-col items-center">
|
||||
<p>Selected date: {singleDate?.toString()}</p>
|
||||
<Calendar
|
||||
value={singleDate}
|
||||
onChange={setSingleDate}
|
||||
onSelect={setSingleDate}
|
||||
/>
|
||||
<div className="flex flex-col gap-10 items-center justify-between">
|
||||
<h1 className="text-2xl font-bold">Badge</h1>
|
||||
<div className="space-y-5">
|
||||
{['primary', 'secondary', 'tertiary', 'inset'].map(
|
||||
(variant, index) => (
|
||||
<div className="flex gap-5" key={index}>
|
||||
{['sm', 'xs'].map((size) => (
|
||||
<Badge
|
||||
key={size}
|
||||
variant={variant as BadgeProps['variant']}
|
||||
size={size as BadgeProps['size']}
|
||||
>
|
||||
1
|
||||
</Badge>
|
||||
))}
|
||||
</div>
|
||||
),
|
||||
)}
|
||||
</div>
|
||||
<div className="space-y-5 flex flex-col items-center">
|
||||
<p>
|
||||
Start date:{' '}
|
||||
{dateRange instanceof Array ? dateRange[0]?.toString() : ''}{' '}
|
||||
<br />
|
||||
End date:{' '}
|
||||
{dateRange instanceof Array ? dateRange[1]?.toString() : ''}
|
||||
</p>
|
||||
<Calendar selectRange value={dateRange} onChange={setDateRange} />
|
||||
</div>
|
||||
|
||||
<div className="w-full h border border-gray-200 px-20 my-10" />
|
||||
|
||||
<div className="flex flex-col gap-10 items-center justify-between">
|
||||
<h1 className="text-2xl font-bold">Checkbox</h1>
|
||||
<div className="flex gap-10 flex-wrap">
|
||||
{Array.from({ length: 5 }).map((_, index) => (
|
||||
<Checkbox
|
||||
id={`checkbox-${index + 1}`}
|
||||
key={index}
|
||||
label={`Label ${index + 1}`}
|
||||
disabled={index === 2 || index === 4 ? true : false}
|
||||
checked={index === 4 ? true : undefined}
|
||||
value={`value-${index + 1}`}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
<div className="flex gap-10 flex-wrap">
|
||||
{Array.from({ length: 2 }).map((_, index) => (
|
||||
<Checkbox
|
||||
id={`checkbox-description-${index + 1}`}
|
||||
key={index}
|
||||
label={`Label ${index + 1}`}
|
||||
description={`Description of the checkbox ${index + 1}`}
|
||||
value={`value-with-description-${index + 1}`}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="w-full h border border-gray-200 px-20 my-10" />
|
||||
|
||||
<div className="flex flex-col gap-10 items-center justify-between">
|
||||
<h1 className="text-2xl font-bold">Calendar</h1>
|
||||
<div className="flex flex-col gap-10">
|
||||
<div className="space-y-5 flex flex-col items-center">
|
||||
<p>Selected date: {singleDate?.toString()}</p>
|
||||
<Calendar
|
||||
value={singleDate}
|
||||
onChange={setSingleDate}
|
||||
onSelect={setSingleDate}
|
||||
/>
|
||||
</div>
|
||||
<div className="space-y-5 flex flex-col items-center">
|
||||
<p>
|
||||
Start date:{' '}
|
||||
{dateRange instanceof Array ? dateRange[0]?.toString() : ''}{' '}
|
||||
<br />
|
||||
End date:{' '}
|
||||
{dateRange instanceof Array ? dateRange[1]?.toString() : ''}
|
||||
</p>
|
||||
<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>
|
||||
|
||||
Reference in New Issue
Block a user