diff --git a/src/components/shared/Avatar/Avatar.styles.ts b/src/components/shared/Avatar/Avatar.styles.ts new file mode 100644 index 00000000..e374695c --- /dev/null +++ b/src/components/shared/Avatar/Avatar.styles.ts @@ -0,0 +1,10 @@ +import styled from '@emotion/styled'; + +import { Image } from '@components/shared/Image'; + +import { AvatarProps } from './Avatar'; + +export const StyledImage = styled(Image)` + border: ${({ border }) => border}; + border-radius: ${({ radius }) => radius}; +`; diff --git a/src/components/shared/Avatar/Avatar.tsx b/src/components/shared/Avatar/Avatar.tsx new file mode 100644 index 00000000..961588d8 --- /dev/null +++ b/src/components/shared/Avatar/Avatar.tsx @@ -0,0 +1,29 @@ +import { HTMLAttributes } from 'react'; + +import { StyledImage } from './Avatar.styles'; + +export type AvatarProps = { + src: string; + size?: number; + radius?: string; + border?: string; +} & HTMLAttributes; + +export const Avatar = ({ + src, + size = 30, + radius = '50%', + border, + ...props +}: AvatarProps) => { + return ( + + ); +}; diff --git a/src/components/shared/Avatar/index.ts b/src/components/shared/Avatar/index.ts new file mode 100644 index 00000000..27700fe3 --- /dev/null +++ b/src/components/shared/Avatar/index.ts @@ -0,0 +1 @@ +export * from './Avatar'; diff --git a/src/components/shared/AvatarGroup/AvatarGroup.styles.ts b/src/components/shared/AvatarGroup/AvatarGroup.styles.ts new file mode 100644 index 00000000..92e17d78 --- /dev/null +++ b/src/components/shared/AvatarGroup/AvatarGroup.styles.ts @@ -0,0 +1,17 @@ +import styled from '@emotion/styled'; + +import { Avatar, AvatarProps } from '@components/shared/Avatar'; + +import { AvatarGroupProps } from './AvatarGroup'; + +type AvatarGroupWrapperProps = Required>; + +export const AvatarGroupWrapper = styled.div` + padding-left: ${({ overlap }) => `${overlap / 16}rem`}; +`; + +export const OverlapedAvatar = styled(Avatar)< + AvatarProps & { overlap: number } +>` + margin-left: ${({ overlap }) => `-${overlap / 16}rem`}; +`; diff --git a/src/components/shared/AvatarGroup/AvatarGroup.tsx b/src/components/shared/AvatarGroup/AvatarGroup.tsx new file mode 100644 index 00000000..01d22487 --- /dev/null +++ b/src/components/shared/AvatarGroup/AvatarGroup.tsx @@ -0,0 +1,45 @@ +import React, { HTMLAttributes } from 'react'; + +import { Avatar, AvatarProps } from '../Avatar'; +import { AvatarGroupWrapper, OverlapedAvatar } from './AvatarGroup.styles'; + +export type AvatarGroupProps = { + children: React.ReactNode; + overlap?: number; +} & Omit & + HTMLAttributes; + +export const AvatarGroup = ({ + children, + size = 30, + radius = '50%', + border, + overlap = 10, + ...props +}: AvatarGroupProps) => { + const avatars = React.Children.toArray(children) + .filter((element): element is React.ReactElement => { + if (!React.isValidElement(element)) { + return false; + } + if (element.type !== Avatar) { + return false; + } + return true; + }) + .map(({ props }) => ( + + )); + + return ( + + {avatars} + + ); +}; diff --git a/src/components/shared/AvatarGroup/index.ts b/src/components/shared/AvatarGroup/index.ts new file mode 100644 index 00000000..73dc5e1a --- /dev/null +++ b/src/components/shared/AvatarGroup/index.ts @@ -0,0 +1 @@ +export * from './AvatarGroup'; diff --git a/src/components/shared/Image/Image.styles.ts b/src/components/shared/Image/Image.styles.ts new file mode 100644 index 00000000..a081f0c5 --- /dev/null +++ b/src/components/shared/Image/Image.styles.ts @@ -0,0 +1,12 @@ +import styled from '@emotion/styled'; + +import { ImageCustomProps } from './Image'; + +type ImgProps = Required; + +export const Img = styled.img` + display: ${({ block }) => (block ? 'block' : 'inline')}; + width: ${({ width }) => width}; + height: ${({ height }) => height}; + object-fit: ${({ mode }) => mode}; +`; diff --git a/src/components/shared/Image/Image.tsx b/src/components/shared/Image/Image.tsx new file mode 100644 index 00000000..b03c24e0 --- /dev/null +++ b/src/components/shared/Image/Image.tsx @@ -0,0 +1,40 @@ +import { CSSProperties, HTMLAttributes } from 'react'; + +import { Img } from './Image.styles'; + +export type ImageCustomProps = { + src: string; + block?: boolean; + width: number | string; + height?: number | string; + alt: string; + mode?: CSSProperties['objectFit']; +}; +type ImageProps = ImageCustomProps & HTMLAttributes; + +export const Image = ({ + src, + block = false, + width, + height = width, + alt, + mode = 'cover', + ...props +}: ImageProps) => { + const stringifiedWidth = + typeof width === 'number' ? `${width / 16}rem` : width; + const stringifiedHeight = + typeof height === 'number' ? `${height / 16}rem` : height; + + return ( + {alt} + ); +}; diff --git a/src/components/shared/Image/index.ts b/src/components/shared/Image/index.ts new file mode 100644 index 00000000..4bbac901 --- /dev/null +++ b/src/components/shared/Image/index.ts @@ -0,0 +1 @@ +export * from './Image';