mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-07-23 13:29:02 +08:00
0abadbe7bb
```[tasklist] ### Tasks - [x] Migrate components from [design](https://github.com/toeverything/design) - [x] Replace all imports from `@toeverything/components` - [x] Clean up `@toeverything/components` dependencies - [x] Storybook ``` ### Influence Here are all the components that are influenced by `@toeverything/components` - `@affine/component` - App update `Button` `Tooltip` - App sidebar header `IconButton`, `Tooltip` - Back `Button` - Auth - Change email page save `Button` - Change password page all `Button`s (Save, Later, Open) - Confirm change email `Button` - Set password page `Button` - Sign in success page `Button` - Sign up page `Button` - Auth `Modal` - Workspace card `Avatar`, `Divider`, `Tooltip`, `IconButton` - Share - Disable shared public link `Modal` - Import page `IconButton`, `Tooltip` - Accept invite page `Avatar`, `Button` - Invite member `Modal` - 404 Page `Avatar`, `Button`, `IconButton`, `Tooltip` - Notification center `IconButton` - Page list - operation cell `IconButton`, `Menu`, `ConfirmModal`, `Tooltip` - tags more `Menu` - favorite `IconButton`, `Tooltip` - new page dropdown `Menu` - filter `Menu`, `Button`, `IconButton` - Page operation `Menu` - export `MenuItem` - move to trash `MenuItem`, `ConfirmModal` - Workspace header filter `Menu`, `Button` - Collection bar `Button`, `Tooltip` (*⚠️ seems not used*) - Collection operation `Menu`, `MenuItem` - Create collection `Modal`, `Button` - Edit collection `Modal`, `Button` - Page mode filter `Menu` - Page mode `Button`, `Menu` - Setting modal - storage usage progress `Button`, `Tooltip` - On boarding tour `Modal` - `@affine/core` - Bookmark `Menu` - Affine error boundary `Button` - After sign in send email `Button` - After sign up send email `Button` - Send email `Button` - Sign in `Button` - Subscription redirect `Loading`, `Button` - Setting `Modal` - User plan button `Tooltip` - Members `Avatar`, `Button`, `IconButton`, `Loading`, `Tooltip`, `Menu` - Profile `Button`, `Avatar` - Workspace - publish panel `Button`, `Tooltip` - export panel `Button` - storage panel `Button`, `Tooltip` - delete `ConfirmModal` - Language `Menu` - Account setting `Avatar`, `Button` - Date format setting `Menu` - Billing `Button`, `IconButton`, `Loading` - Payment plans `Button`, `ConfirmModal`, `Modal`, `Tooltip` - Create workspace `Modal`, `ConfirmModal`, `Button` - Payment disabled `ConfirmModal` - Share/Export `Menu`, `Button`, `Divider` - Sign out `ConfirmModal` - Temp disable affine cloud `Modal` - Page detail operation `Menu` - Blocksuite mode switch `Tooltip` - Login card `Avatar` - Help island `Tooltip` - `plugin` - copilot - hello world - image preview - outline
144 lines
4.1 KiB
TypeScript
144 lines
4.1 KiB
TypeScript
import { CloseIcon } from '@blocksuite/icons';
|
|
import {
|
|
type AvatarFallbackProps,
|
|
type AvatarImageProps,
|
|
type AvatarProps as RadixAvatarProps,
|
|
Fallback as AvatarFallback,
|
|
Image as AvatarImage,
|
|
Root as AvatarRoot,
|
|
} from '@radix-ui/react-avatar';
|
|
import { assignInlineVars } from '@vanilla-extract/dynamic';
|
|
import clsx from 'clsx';
|
|
import type { CSSProperties, HTMLAttributes, MouseEvent } from 'react';
|
|
import { forwardRef, type ReactElement, useMemo, useState } from 'react';
|
|
|
|
import { IconButton } from '../button';
|
|
import { Tooltip, type TooltipProps } from '../tooltip';
|
|
import { ColorfulFallback } from './colorful-fallback';
|
|
import * as style from './style.css';
|
|
import { sizeVar } from './style.css';
|
|
|
|
export type AvatarProps = {
|
|
size?: number;
|
|
url?: string | null;
|
|
name?: string;
|
|
className?: string;
|
|
style?: CSSProperties;
|
|
colorfulFallback?: boolean;
|
|
hoverIcon?: ReactElement;
|
|
onRemove?: (e: MouseEvent<HTMLButtonElement>) => void;
|
|
avatarTooltipOptions?: Omit<TooltipProps, 'children'>;
|
|
removeTooltipOptions?: Omit<TooltipProps, 'children'>;
|
|
|
|
fallbackProps?: AvatarFallbackProps;
|
|
imageProps?: Omit<AvatarImageProps, 'src'>;
|
|
avatarProps?: RadixAvatarProps;
|
|
hoverWrapperProps?: HTMLAttributes<HTMLDivElement>;
|
|
removeButtonProps?: HTMLAttributes<HTMLButtonElement>;
|
|
} & HTMLAttributes<HTMLSpanElement>;
|
|
|
|
export const Avatar = forwardRef<HTMLSpanElement, AvatarProps>(
|
|
(
|
|
{
|
|
size = 20,
|
|
style: propsStyles = {},
|
|
url,
|
|
name,
|
|
className,
|
|
colorfulFallback = false,
|
|
hoverIcon,
|
|
fallbackProps: { className: fallbackClassName, ...fallbackProps } = {},
|
|
imageProps,
|
|
avatarProps,
|
|
onRemove,
|
|
hoverWrapperProps: {
|
|
className: hoverWrapperClassName,
|
|
...hoverWrapperProps
|
|
} = {},
|
|
avatarTooltipOptions,
|
|
removeTooltipOptions,
|
|
removeButtonProps: {
|
|
className: removeButtonClassName,
|
|
...removeButtonProps
|
|
} = {},
|
|
...props
|
|
},
|
|
ref
|
|
) => {
|
|
const firstCharOfName = useMemo(() => {
|
|
return name?.slice(0, 1) || 'A';
|
|
}, [name]);
|
|
const [imageDom, setImageDom] = useState<HTMLDivElement | null>(null);
|
|
const [removeButtonDom, setRemoveButtonDom] =
|
|
useState<HTMLButtonElement | null>(null);
|
|
|
|
return (
|
|
<AvatarRoot className={style.avatarRoot} {...avatarProps} ref={ref}>
|
|
<Tooltip
|
|
portalOptions={{ container: imageDom }}
|
|
{...avatarTooltipOptions}
|
|
>
|
|
<div
|
|
ref={setImageDom}
|
|
className={clsx(style.avatarWrapper, className)}
|
|
style={{
|
|
...assignInlineVars({
|
|
[sizeVar]: size ? `${size}px` : '20px',
|
|
}),
|
|
...propsStyles,
|
|
}}
|
|
{...props}
|
|
>
|
|
<AvatarImage
|
|
className={style.avatarImage}
|
|
src={url || ''}
|
|
alt={name}
|
|
{...imageProps}
|
|
/>
|
|
|
|
<AvatarFallback
|
|
className={clsx(style.avatarFallback, fallbackClassName)}
|
|
delayMs={url ? 600 : undefined}
|
|
{...fallbackProps}
|
|
>
|
|
{colorfulFallback ? (
|
|
<ColorfulFallback char={firstCharOfName} />
|
|
) : (
|
|
firstCharOfName
|
|
)}
|
|
</AvatarFallback>
|
|
{hoverIcon ? (
|
|
<div
|
|
className={clsx(style.hoverWrapper, hoverWrapperClassName)}
|
|
{...hoverWrapperProps}
|
|
>
|
|
{hoverIcon}
|
|
</div>
|
|
) : null}
|
|
</div>
|
|
</Tooltip>
|
|
|
|
{onRemove ? (
|
|
<Tooltip
|
|
portalOptions={{ container: removeButtonDom }}
|
|
{...removeTooltipOptions}
|
|
>
|
|
<IconButton
|
|
size="extraSmall"
|
|
type="default"
|
|
className={clsx(style.removeButton, removeButtonClassName)}
|
|
onClick={onRemove}
|
|
ref={setRemoveButtonDom}
|
|
{...removeButtonProps}
|
|
>
|
|
<CloseIcon />
|
|
</IconButton>
|
|
</Tooltip>
|
|
) : null}
|
|
</AvatarRoot>
|
|
);
|
|
}
|
|
);
|
|
|
|
Avatar.displayName = 'Avatar';
|