Skip to content

Commit

Permalink
♻️ refactor(avatar): refactor Avatar component
Browse files Browse the repository at this point in the history
  • Loading branch information
canisminor1990 committed Jun 9, 2023
1 parent 14e4cce commit 0cf2963
Show file tree
Hide file tree
Showing 9 changed files with 151 additions and 125 deletions.
62 changes: 36 additions & 26 deletions src/ActionIcon/index.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Loader2, LucideIcon } from 'lucide-react';
import { memo } from 'react';
import { memo, useMemo } from 'react';

import { Icon, Spotlight, Tooltip, TooltipProps } from '@/index';
import { DivProps } from '@/types';
Expand All @@ -18,6 +18,39 @@ export type ActionIconSize =
strokeWidth?: number;
};

const calcSize = (size?: ActionIconSize) => {
let blockSize: number;
let borderRadius: number;

switch (size) {
case 'large':
blockSize = 44;
borderRadius = 8;
break;
case 'normal':
blockSize = 36;
borderRadius = 5;
break;
case 'small':
blockSize = 24;
borderRadius = 5;
break;
case 'site':
blockSize = 34;
borderRadius = 5;
break;
default:
blockSize = size?.blockSize || 36;
borderRadius = size?.borderRadius || 5;
break;
}

return {
blockSize,
borderRadius,
};
};

export interface ActionIconProps extends DivProps {
/**
* @description Whether the icon is active or not
Expand Down Expand Up @@ -88,31 +121,8 @@ const ActionIcon = memo<ActionIconProps>(
...props
}) => {
const { styles, cx } = useStyles({ active: Boolean(active), glass: Boolean(glass) });
let blockSize: number;
let borderRadius: number;

switch (size) {
case 'large':
blockSize = 44;
borderRadius = 8;
break;
case 'normal':
blockSize = 36;
borderRadius = 5;
break;
case 'small':
blockSize = 24;
borderRadius = 5;
break;
case 'site':
blockSize = 34;
borderRadius = 5;
break;
default:
blockSize = size?.blockSize || 36;
borderRadius = size?.borderRadius || 5;
break;
}

const { blockSize, borderRadius } = useMemo(() => calcSize(size), [size]);

const content = (
<>
Expand Down
28 changes: 26 additions & 2 deletions src/Avatar/demos/index.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,29 @@
import { Avatar } from '@lobehub/ui';
import { ActionIconProps, Avatar, StroyBook, useControls, useCreateStore } from '@lobehub/ui';

export default () => {
return <Avatar avatar={'https://raw.githubusercontent.com/lobehub/favicon/main/icon.png'} />;
const store = useCreateStore();
const control: ActionIconProps | any = useControls(
{
avatar: 'https://raw.githubusercontent.com/lobehub/favicon/main/icon.png',
background: '#FEE064',
size: {
value: 40,
step: 1,
min: 16,
max: 128,
},
shape: {
value: 'circle',
options: ['circle', 'square'],
},
title: 'cm',
},
{ store },
);

return (
<StroyBook levaStore={store}>
<Avatar {...control} />
</StroyBook>
);
};
2 changes: 1 addition & 1 deletion src/Avatar/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ description: Avatar is a component that displays a user's profile picture or ini

## Default

<code src="./demos/index.tsx" center></code>
<code src="./demos/index.tsx" nopadding></code>

## Emoji

Expand Down
66 changes: 23 additions & 43 deletions src/Avatar/index.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
import { Avatar as A } from 'antd';
import { FC } from 'react';
import { Center } from 'react-layout-kit';
import { Avatar as AntAvatar, type AvatarProps as AntAvatarProps } from 'antd';
import { memo } from 'react';

import { useStyles } from './style';

export interface AvatarProps {
export interface AvatarProps extends AntAvatarProps {
/**
* @description The URL or base64 data of the avatar image
*/
Expand All @@ -29,46 +28,27 @@ export interface AvatarProps {
title?: string;
}

const Avatar: FC<AvatarProps> = ({ avatar, title, size = 40, shape = 'circle', background }) => {
const { styles, theme } = useStyles();
const Avatar = memo<AvatarProps>(
({ className, avatar, title, size = 40, shape = 'circle', background, ...props }) => {
const { styles, cx } = useStyles({ background, size });

const backgroundColor = background ?? theme.colorBgContainer;
const isImage = avatar && ['/', 'http', 'data:'].some((i) => avatar.startsWith(i));
const text = isImage ? title : avatar;

const isImage = avatar && ['/', 'http', 'data:'].some((i) => avatar.startsWith(i));

return (
<Center
className={styles.container}
style={{
width: size,
height: size,
borderRadius: shape === 'circle' ? '50%' : 6,
backgroundColor,
borderWidth: isImage ? 1 : 0,
}}
>
{!avatar ? (
<A shape={shape} size={size}>
{title?.slice(0, 2)}
</A>
) : isImage ? (
<A className={styles.border} shape={shape} size={size} src={avatar} />
) : (
<Center
className={styles.border}
style={{
width: size,
height: size,
fontSize: size / 2,
borderRadius: shape === 'circle' ? '50%' : 6,
backgroundColor,
}}
>
{avatar}
</Center>
)}
</Center>
);
};
return !isImage ? (
<AntAvatar className={cx(styles.avatar, className)} shape={shape} size={size} {...props}>
{text?.toUpperCase().slice(0, 2)}
</AntAvatar>
) : (
<AntAvatar
className={cx(styles.avatar, className)}
shape={shape}
size={size}
src={avatar}
{...props}
/>
);
},
);

export default Avatar;
37 changes: 25 additions & 12 deletions src/Avatar/style.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,27 @@
import { createStyles } from 'antd-style';
import { readableColor } from 'polished';

export const useStyles = createStyles(({ css, token }) => ({
container: css`
cursor: pointer;
> * {
cursor: pointer;
}
`,
border: css`
border: 1px solid ${token.colorSplit};
`,
}));
export const useStyles = createStyles(
({ css, token }, { background, size }: { background?: string; size: number }) => {
const backgroundColor = background ?? token.colorBgContainer;
const color = readableColor(backgroundColor);

return {
avatar: css`
cursor: pointer;
font-size: ${size * 0.5}px;
font-weight: 700;
line-height: 1;
color: ${color};
background: ${backgroundColor};
border: 1px solid ${background ? 'transparent' : token.colorSplit};
> * {
cursor: pointer;
}
`,
};
},
);
54 changes: 29 additions & 25 deletions src/Icon/index.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { LucideIcon } from 'lucide-react';
import { memo } from 'react';
import { memo, useMemo } from 'react';

import { DivProps } from '@/types';

Expand All @@ -14,28 +14,7 @@ export type IconSize =
strokeWidth?: number;
};

export interface IconProps extends DivProps {
/**
* @description The icon element to be rendered
* @type LucideIcon
*/
icon: LucideIcon;
/**
* @description Size of the icon
* @default 'normal'
*/
size?: IconSize;
/**
* @description Rotate icon with animation
* @default false
*/
spin?: boolean;
}

const Icon = memo<IconProps>(({ icon, size, style, className, spin, ...props }) => {
const { styles, cx } = useStyles();
const SvgIcon = icon;

const calcSize = (size?: IconSize) => {
let fontSize: number | string;
let strokeWidth: number;

Expand All @@ -60,13 +39,38 @@ const Icon = memo<IconProps>(({ icon, size, style, className, spin, ...props })
fontSize = '1em';
strokeWidth = 2;
}

break;
}
return { fontSize, strokeWidth };
};

export interface IconProps extends DivProps {
/**
* @description The icon element to be rendered
* @type LucideIcon
*/
icon: LucideIcon;
/**
* @description Size of the icon
* @default 'normal'
*/
size?: IconSize;
/**
* @description Rotate icon with animation
* @default false
*/
spin?: boolean;
}

const Icon = memo<IconProps>(({ icon, size, style, className, spin, ...props }) => {
const { styles, cx } = useStyles();
const SvgIcon = icon;

const { fontSize, strokeWidth } = useMemo(() => calcSize(size), [size]);

return (
<div
className={cx(spin && styles.spin, className)}
className={cx(styles.icon, spin && styles.spin, className)}
style={{ width: fontSize, height: fontSize, ...style }}
{...props}
>
Expand Down
5 changes: 5 additions & 0 deletions src/Icon/style.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,11 @@ export const useStyles = createStyles(({ css }) => {
}
`;
return {
icon: css`
display: flex;
align-items: center;
justify-content: center;
`,
spin: css`
animation: ${spin} 1s linear infinite;
`,
Expand Down
8 changes: 6 additions & 2 deletions src/Input/style.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,20 +17,24 @@ export const useStyles = createStyles(
height: 36px;
padding: 0 12px;
transition: background-color 100ms ${token.motionEaseOut};
transition: background-color 100ms ${token.motionEaseOut},
border-color 200ms ${token.motionEaseOut};
input {
background: transparent;
}
&:hover {
background-color: ${token.colorFillTertiary};
border-color: ${token.colorBorder};
}
&:focus {
border-color: ${token.colorTextQuaternary};
}
&.ant-input-affix-wrapper-focused {
border-color: ${token.colorTextQuaternary};
}
`,
),
textarea: cx(
Expand Down
14 changes: 0 additions & 14 deletions src/SearchBar/style.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,6 @@ export const useStyles = createStyles(({ css, token }) => ({
search: css`
position: relative;
max-width: 100%;
.ant-input-affix-wrapper {
&:hover {
border-color: ${token.colorBorder};
}
}
.ant-input-affix-wrapper-focused {
border-color: ${token.colorTextQuaternary};
&:hover {
border-color: ${token.colorTextQuaternary};
}
}
`,
input: css`
position: relative;
Expand Down

1 comment on commit 0cf2963

@vercel
Copy link

@vercel vercel bot commented on 0cf2963 Jun 9, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Successfully deployed to the following URLs:

lobe-ui – ./

lobe-ui-git-master-lobehub.vercel.app
lobe-ui.vercel.app
lobe-ui-lobehub.vercel.app
ui.lobehub.com

Please sign in to comment.