mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-07-22 12:36:24 +08:00
refactor(component): migrate design components (#5000)
```[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
This commit is contained in:
@@ -0,0 +1,47 @@
|
||||
import { DialogTrigger } from '@radix-ui/react-dialog';
|
||||
import clsx from 'clsx';
|
||||
|
||||
import type { ButtonProps } from '../button';
|
||||
import { Button } from '../button';
|
||||
import { Modal, type ModalProps } from './modal';
|
||||
import * as styles from './styles.css';
|
||||
|
||||
export interface ConfirmModalProps extends ModalProps {
|
||||
confirmButtonOptions?: ButtonProps;
|
||||
onConfirm?: () => void;
|
||||
cancelText?: string;
|
||||
cancelButtonOptions?: ButtonProps;
|
||||
}
|
||||
|
||||
export const ConfirmModal = ({
|
||||
children,
|
||||
confirmButtonOptions,
|
||||
// FIXME: we need i18n
|
||||
cancelText = 'Cancel',
|
||||
cancelButtonOptions,
|
||||
onConfirm,
|
||||
width = 480,
|
||||
...props
|
||||
}: ConfirmModalProps) => {
|
||||
return (
|
||||
<Modal
|
||||
contentOptions={{ className: styles.confirmModalContainer }}
|
||||
width={width}
|
||||
{...props}
|
||||
>
|
||||
{children ? (
|
||||
<div className={styles.confirmModalContent}>{children}</div>
|
||||
) : null}
|
||||
<div
|
||||
className={clsx(styles.modalFooter, {
|
||||
modalFooterWithChildren: !!children,
|
||||
})}
|
||||
>
|
||||
<DialogTrigger asChild>
|
||||
<Button {...cancelButtonOptions}>{cancelText}</Button>
|
||||
</DialogTrigger>
|
||||
<Button onClick={onConfirm} {...confirmButtonOptions}></Button>
|
||||
</div>
|
||||
</Modal>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,2 @@
|
||||
export * from './confirm-modal';
|
||||
export * from './modal';
|
||||
@@ -0,0 +1,111 @@
|
||||
import { CloseIcon } from '@blocksuite/icons';
|
||||
import type {
|
||||
DialogContentProps,
|
||||
DialogOverlayProps,
|
||||
DialogPortalProps,
|
||||
DialogProps,
|
||||
} from '@radix-ui/react-dialog';
|
||||
import * as Dialog from '@radix-ui/react-dialog';
|
||||
import { assignInlineVars } from '@vanilla-extract/dynamic';
|
||||
import clsx from 'clsx';
|
||||
import { type CSSProperties, forwardRef } from 'react';
|
||||
|
||||
import { IconButton, type IconButtonProps } from '../button';
|
||||
import * as styles from './styles.css';
|
||||
|
||||
export interface ModalProps extends DialogProps {
|
||||
width?: CSSProperties['width'];
|
||||
height?: CSSProperties['height'];
|
||||
minHeight?: CSSProperties['minHeight'];
|
||||
title?: string;
|
||||
description?: string;
|
||||
withoutCloseButton?: boolean;
|
||||
|
||||
portalOptions?: DialogPortalProps;
|
||||
contentOptions?: DialogContentProps;
|
||||
overlayOptions?: DialogOverlayProps;
|
||||
closeButtonOptions?: IconButtonProps;
|
||||
}
|
||||
|
||||
const getVar = (style: number | string = '', defaultValue = '') => {
|
||||
return style
|
||||
? typeof style === 'number'
|
||||
? `${style}px`
|
||||
: style
|
||||
: defaultValue;
|
||||
};
|
||||
|
||||
export const Modal = forwardRef<HTMLDivElement, ModalProps>(
|
||||
(
|
||||
{
|
||||
width,
|
||||
height,
|
||||
minHeight = 194,
|
||||
title,
|
||||
description,
|
||||
withoutCloseButton = false,
|
||||
|
||||
portalOptions,
|
||||
contentOptions: {
|
||||
style: contentStyle,
|
||||
className: contentClassName,
|
||||
...otherContentOptions
|
||||
} = {},
|
||||
overlayOptions: {
|
||||
className: overlayClassName,
|
||||
...otherOverlayOptions
|
||||
} = {},
|
||||
closeButtonOptions = {},
|
||||
children,
|
||||
...props
|
||||
},
|
||||
ref
|
||||
) => (
|
||||
<Dialog.Root {...props}>
|
||||
<Dialog.Portal {...portalOptions}>
|
||||
<Dialog.Overlay
|
||||
className={clsx(styles.modalOverlay, overlayClassName)}
|
||||
{...otherOverlayOptions}
|
||||
/>
|
||||
<Dialog.Content
|
||||
className={clsx(styles.modalContent, contentClassName)}
|
||||
style={{
|
||||
...assignInlineVars({
|
||||
[styles.widthVar]: getVar(width, '50vw'),
|
||||
[styles.heightVar]: getVar(height, 'unset'),
|
||||
[styles.minHeightVar]: getVar(minHeight, '26px'),
|
||||
}),
|
||||
...contentStyle,
|
||||
}}
|
||||
{...otherContentOptions}
|
||||
ref={ref}
|
||||
>
|
||||
{withoutCloseButton ? null : (
|
||||
<Dialog.Close asChild>
|
||||
<IconButton
|
||||
className={styles.closeButton}
|
||||
aria-label="Close"
|
||||
type="plain"
|
||||
{...closeButtonOptions}
|
||||
>
|
||||
<CloseIcon />
|
||||
</IconButton>
|
||||
</Dialog.Close>
|
||||
)}
|
||||
{title ? (
|
||||
<Dialog.Title className={styles.modalHeader}>{title}</Dialog.Title>
|
||||
) : null}
|
||||
{description ? (
|
||||
<Dialog.Description className={styles.modalDescription}>
|
||||
{description}
|
||||
</Dialog.Description>
|
||||
) : null}
|
||||
|
||||
{children}
|
||||
</Dialog.Content>
|
||||
</Dialog.Portal>
|
||||
</Dialog.Root>
|
||||
)
|
||||
);
|
||||
|
||||
Modal.displayName = 'Modal';
|
||||
@@ -0,0 +1,79 @@
|
||||
import { createVar, style } from '@vanilla-extract/css';
|
||||
|
||||
export const widthVar = createVar('widthVar');
|
||||
export const heightVar = createVar('heightVar');
|
||||
export const minHeightVar = createVar('minHeightVar');
|
||||
|
||||
export const modalOverlay = style({
|
||||
position: 'fixed',
|
||||
inset: 0,
|
||||
backgroundColor: 'var(--affine-background-modal-color)',
|
||||
zIndex: 'var(--affine-z-index-modal)',
|
||||
});
|
||||
|
||||
export const modalContent = style({
|
||||
vars: {
|
||||
[widthVar]: '',
|
||||
[heightVar]: '',
|
||||
[minHeightVar]: '',
|
||||
},
|
||||
width: widthVar,
|
||||
height: heightVar,
|
||||
minHeight: minHeightVar,
|
||||
boxSizing: 'border-box',
|
||||
fontSize: 'var(--affine-font-base)',
|
||||
fontWeight: '400',
|
||||
lineHeight: '1.6',
|
||||
padding: '20px 24px',
|
||||
backgroundColor: 'var(--affine-background-overlay-panel-color)',
|
||||
boxShadow: 'var(--affine-popover-shadow)',
|
||||
borderRadius: '12px',
|
||||
maxHeight: 'calc(100vh - 32px)',
|
||||
// :focus-visible will set outline
|
||||
outline: 'none',
|
||||
position: 'fixed',
|
||||
zIndex: 'var(--affine-z-index-modal)',
|
||||
top: ' 50%',
|
||||
left: '50%',
|
||||
transform: 'translate(-50%, -50%)',
|
||||
});
|
||||
|
||||
export const closeButton = style({
|
||||
position: 'absolute',
|
||||
top: '22px',
|
||||
right: '20px',
|
||||
});
|
||||
|
||||
export const modalHeader = style({
|
||||
fontSize: 'var(--affine-font-h-6)',
|
||||
fontWeight: '600',
|
||||
lineHeight: '1.45',
|
||||
marginBottom: '12px',
|
||||
});
|
||||
export const modalDescription = style({
|
||||
// marginBottom: '20px',
|
||||
});
|
||||
|
||||
export const modalFooter = style({
|
||||
display: 'flex',
|
||||
justifyContent: 'flex-end',
|
||||
alignItems: 'center',
|
||||
paddingTop: '40px',
|
||||
marginTop: 'auto',
|
||||
gap: '20px',
|
||||
selectors: {
|
||||
'&.modalFooterWithChildren': {
|
||||
paddingTop: '20px',
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
export const confirmModalContent = style({
|
||||
marginTop: '12px',
|
||||
marginBottom: '20px',
|
||||
});
|
||||
|
||||
export const confirmModalContainer = style({
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
});
|
||||
Reference in New Issue
Block a user