Skip to content
This repository has been archived by the owner on May 18, 2024. It is now read-only.

Mint Phase 4 #393

Open
wants to merge 10 commits into
base: develop
Choose a base branch
from
2 changes: 1 addition & 1 deletion packages/ui-toolkit/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@groww-tech/ui-toolkit",
"version": "0.5.4",
"version": "0.5.5",
"description": "A lightning nature UI",
"main": "dist/cjs/index.js",
"module": "dist/esm/index.js",
Expand Down
183 changes: 183 additions & 0 deletions packages/ui-toolkit/src/components/atoms/Avatar/Avatar.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,183 @@
import React, { useState, useEffect } from 'react';
import cn from 'classnames';
import { AccountBalance } from '@groww-tech/icon-store/mi';

import { AVATAR_SHAPES, AVATAR_SIZES } from './avatar.constants';

import './avatar.css';


function getInitials(name: string) {
const names = name.trim().split(' ');
const firstName = names[0] ?? '';
const lastName = names.length > 1 ? names[names.length - 1] : '';

const initial = firstName && lastName ? `${firstName.charAt(0)}${lastName.charAt(0)}` : firstName.charAt(0);

return initial.toUpperCase();
}


type Status = 'loading' | 'failed' | 'pending' | 'loaded';


const useImage = (src: string): Status => {
const [ status, setStatus ] = useState<Status>('pending');

useEffect(() => {
const image = new Image();

image.src = src;

image.onload = () => {
setStatus('loaded');
};

image.onerror = () => {
setStatus('failed');
};

setStatus('loading');

return () => {
// Cleanup on unmount or if src changes
image.onload = null;
image.onerror = null;
};
}, [ src ]);

return status;
};


const Avatar = (props: Props) => {
const { src, name, size, isDisabled, shape } = props;

const status = useImage(src);

const initials = getInitials(name);


const getAvatarSize = () => {
switch (size) {
case AVATAR_SIZES.XSMALL:
return 24;

case AVATAR_SIZES.SMALL:
return 32;

case AVATAR_SIZES.BASE:
return 40;

case AVATAR_SIZES.LARGE:
return 48;

case AVATAR_SIZES.XLARGE:
return 56;

default:
return 40;
}
};

const avatarSize = getAvatarSize();

const avatarDimensionClasses = cn(`av91Avatar${size}`);

const avatarInitialClasses = cn('av91AvatarInitialContainer absolute-center circle');

const avatarImgClasses = cn({
circle: shape === AVATAR_SHAPES.CIRCULAR,
av91AvatarRectangular: shape === AVATAR_SHAPES.RECTANGULAR,
av91AvatarImageDisabled: isDisabled
});

const avatarIconContainerClasses = cn('av91AvatarInitialContainer absolute-center', {
av91AvatarRectangular: shape === AVATAR_SHAPES.RECTANGULAR,
backgroundTertiary: !isDisabled,
backgroundSecondary: isDisabled
});

const avatarIconClasses = cn({
contentDisabled: isDisabled,
contentSecondary: !isDisabled
});

const backgroundClasses = cn({
backgroundAccentSubtle: !isDisabled,
backgroundTertiary: isDisabled
});

const fontClasses = cn({
bodySmallHeavy: size === AVATAR_SIZES.XSMALL || size === AVATAR_SIZES.SMALL,
bodyBaseHeavy: size === AVATAR_SIZES.BASE,
bodyLargeHeavy: size === AVATAR_SIZES.LARGE,
bodyXLargeHeavy: size === AVATAR_SIZES.XLARGE,
contentPositive: !isDisabled,
contentDisabled: isDisabled
});

const avatarInitial = (
<div role="img"
className={cn(avatarInitialClasses, fontClasses, backgroundClasses, avatarDimensionClasses)}
>
{initials}
</div>
);

const avatarIcon = (
<div role="img"
className={cn(avatarIconContainerClasses, avatarDimensionClasses)}
>
<AccountBalance className={avatarIconClasses} />
</div>
);

const avatarImage = <img className={avatarImgClasses}
src={src}
alt={name}
height={avatarSize}
width={avatarSize}
/>;
const avatarDisabled = shape === AVATAR_SHAPES.RECTANGULAR ? avatarIcon : avatarInitial;

switch (status) {
case 'loading':
return avatarDisabled;

case 'failed':
return avatarDisabled;

case 'loaded':
return avatarImage;

case 'pending':
return avatarDisabled;

default:
return avatarDisabled;
}
};


type RequiredProps = {
name: string;
src: string;
};


type DefaultProps = {
size: ValueOf<typeof AVATAR_SIZES>;
shape: ValueOf<typeof AVATAR_SHAPES>;
isDisabled: boolean;
};

export type Props = RequiredProps & DefaultProps & Partial<HTMLImageElement>;

Avatar.defaultProps = {
isDisabled: false,
size: AVATAR_SIZES.BASE,
shape: AVATAR_SHAPES.CIRCULAR
} as DefaultProps;

export default Avatar;
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
export const AVATAR_SHAPES = {
CIRCULAR: 'Circular',
RECTANGULAR: 'Rectangular'
} as const;

export const AVATAR_SIZES = {
XSMALL: 'XSmall',
SMALL: 'Small',
BASE: 'Base',
LARGE: 'Large',
XLARGE: 'XLarge'
} as const;
37 changes: 37 additions & 0 deletions packages/ui-toolkit/src/components/atoms/Avatar/avatar.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
.av91AvatarInitialContainer {
width: fit-content;
height: fit-content;
}

.av91AvatarRectangular {
border-radius: 8px;
}

.av91AvatarImageDisabled {
filter: grayscale(100%) opacity(50%);
}

.av91AvatarXSmall {
height: 24px;
width: 24px;
}

.av91AvatarSmall {
height: 32px;
width: 32px;
}

.av91AvatarBase {
height: 40px;
width: 40px;
}

.av91AvatarLarge {
height: 48px;
width: 48px;
}

.av91AvatarXLarge {
height: 56px;
width: 56px;
}
1 change: 1 addition & 0 deletions packages/ui-toolkit/src/components/atoms/Avatar/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default as Avatar } from './Avatar';
12 changes: 12 additions & 0 deletions packages/ui-toolkit/src/components/atoms/Divider/Divider.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import React from 'react';

import './divider.css';


const Divider = () => {
return <hr aria-orientation="horizontal"
className="borderPrimary divider23divider"
/>;
};

export default Divider;
5 changes: 5 additions & 0 deletions packages/ui-toolkit/src/components/atoms/Divider/divider.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
.divider23divider {
border-top: 1px;
width: 100%;
margin: 0;
}
1 change: 1 addition & 0 deletions packages/ui-toolkit/src/components/atoms/Divider/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default as Divider } from './Divider';
61 changes: 61 additions & 0 deletions packages/ui-toolkit/src/components/atoms/IconView/IconView.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import React from 'react';
import cn from 'classnames';

import { ReactIconProps } from '@groww-tech/icon-store';

import { ICON_VIEW_SIZES } from './iconView.constants';

import './iconView.css';

export default function IconView(props: Props) {
const { iconComponent, size, isContained } = props;


const getIconSize = () => {
switch (size) {
case ICON_VIEW_SIZES.SMALL:
return 16;

case ICON_VIEW_SIZES.BASE:
return 20;

case ICON_VIEW_SIZES.LARGE:
return 24;

case ICON_VIEW_SIZES.XLARGE:
return 28;

default:
return 20;
}
};

const iconProps = {
className: 'absolute-center',
size: getIconSize()
};

const iconViewClasses = cn(`iv98IconContainer iv98${size} circle`, {
backgroundTertiary: isContained
});

return <div className={iconViewClasses}>{iconComponent(iconProps as ReactIconProps)}</div>;
}


type RequiredProps = {
iconComponent: (props: ReactIconProps) => JSX.Element;
};


type DefaultProps = {
size?: ValueOf<typeof ICON_VIEW_SIZES>;
isContained?: boolean;
};

IconView.defaultProps = {
size: ICON_VIEW_SIZES.BASE,
isContained: false
};

export type Props = RequiredProps & DefaultProps;
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
export const ICON_VIEW_SIZES = {
SMALL: 'Small',
BASE: 'Base',
LARGE: 'Large',
XLARGE: 'XLarge'
} as const;
20 changes: 20 additions & 0 deletions packages/ui-toolkit/src/components/atoms/IconView/iconView.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
.iv98IconContainer {
width: fit-content;
height: fit-content;
}

.iv98Small {
padding: 8px;
}

.iv98Base {
padding: 10px;
}

.iv98Large {
padding: 12px;
}

.iv98XLarge {
padding: 14px;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default as IconView } from './IconView';
Loading