refactor(component): refactor the implementation of Button and IconButton (#7716)

## Button
- Remove props withoutHoverStyle
   refactor hover impl with independent layer, so that hover-color won't affect the background even if is overridden outside
- Update `type` (renamed to `variant`):
  - remove `processing` and `warning`
  - rename `default` with `secondary`
- Remove `shape` props
- Remove `icon` and `iconPosition`, replaced with `prefix: ReactNode` and `suffix: ReactNode`
- Integrate tooltip for more convenient usage
- New Storybook document
- Focus style

## IconButton
- A Wrapper base on `<Button />`
- Override Button size and variant
  - size: `'12' | '14' | '16' | '20' | '24' | number`
     These presets size are referenced from the design system.
  - variant:  `'plain' | 'solid' | 'danger' | 'custom'`
- Inset icon via Button 's prefix

## Fix
- fix some button related issues
- close AF-1159, AF-1160, AF-1161, AF-1162, AF-1163, AF-1158, AF-1157

## Storybook

![CleanShot 2024-08-03 at 14.57.20@2x.png](https://graphite-user-uploaded-assets-prod.s3.amazonaws.com/LakojjjzZNf6ogjOVwKE/f5a76110-35d0-4082-a940-efc12bed87b0.png)
This commit is contained in:
CatsJuice
2024-08-05 02:57:23 +00:00
parent 10deed94e3
commit 3d855647c7
159 changed files with 1384 additions and 1539 deletions
@@ -1,6 +1,7 @@
import { Button, IconButton } from '@affine/component/ui/button';
import { useI18n } from '@affine/i18n';
import { CloseIcon } from '@blocksuite/icons/rc';
import { cssVar } from '@toeverything/theme';
import { useCallback } from 'react';
import * as styles from './index.css';
@@ -37,11 +38,12 @@ export const LocalDemoTips = ({
</div>
<div className={styles.tipsRightItem}>
<div>
<Button onClick={handleClick}>{buttonLabel}</Button>
</div>
<Button style={{ background: cssVar('white') }} onClick={handleClick}>
{buttonLabel}
</Button>
<IconButton
onClick={onClose}
size="20"
data-testid="local-demo-tips-close-button"
>
<CloseIcon />
@@ -41,7 +41,7 @@ export const MobileNavbar = () => {
onOpenChange: setOpenMenu,
}}
>
<IconButton type="plain" className={styles.iconButton}>
<IconButton variant="plain" size="24" className={styles.iconButton}>
{openMenu ? <CloseIcon /> : <PropertyIcon />}
</IconButton>
</Menu>
@@ -1,5 +1,6 @@
import { useI18n } from '@affine/i18n';
import { ArrowLeftSmallIcon } from '@blocksuite/icons/rc';
import { cssVar } from '@toeverything/theme';
import type { FC } from 'react';
import type { ButtonProps } from '../../ui/button';
@@ -9,15 +10,15 @@ export const BackButton: FC<ButtonProps> = props => {
const t = useI18n();
return (
<Button
type="plain"
variant="plain"
style={{
marginTop: 12,
marginLeft: -5,
paddingLeft: 0,
paddingRight: 5,
color: 'var(--affine-text-secondary-color)',
color: cssVar('textSecondaryColor'),
}}
icon={<ArrowLeftSmallIcon />}
prefix={<ArrowLeftSmallIcon />}
{...props}
>
{t['com.affine.backButton']()}
@@ -58,7 +58,7 @@ export const ChangeEmailPage = ({
disabled={hasSetUp}
/>
<Button
type="primary"
variant="primary"
size="large"
onClick={onContinue}
loading={loading}
@@ -59,7 +59,7 @@ export const ChangePasswordPage: FC<{
}
>
{hasSetUp ? (
<Button type="primary" size="large" onClick={onOpenAffine}>
<Button variant="primary" size="large" onClick={onOpenAffine}>
{t['com.affine.auth.open.affine']()}
</Button>
) : (
@@ -14,7 +14,7 @@ export const ConfirmChangeEmail: FC<{
title={t['com.affine.auth.change.email.page.success.title']()}
subtitle={t['com.affine.auth.change.email.page.success.subtitle']()}
>
<Button type="primary" size="large" onClick={onOpenAffine}>
<Button variant="primary" size="large" onClick={onOpenAffine}>
{t['com.affine.auth.open.affine']()}
</Button>
</AuthPageContainer>
@@ -14,7 +14,7 @@ export const ConfirmChangeEmail: FC<{
title={t['com.affine.auth.change.email.page.success.title']()}
subtitle={t['com.affine.auth.change.email.page.success.subtitle']()}
>
<Button type="primary" size="large" onClick={onOpenAffine}>
<Button variant="primary" size="large" onClick={onOpenAffine}>
{t['com.affine.auth.open.affine']()}
</Button>
</AuthPageContainer>
@@ -219,7 +219,7 @@ export const OnboardingPage = ({
</Button>
<Button
className={styles.button}
type="primary"
variant="primary"
size="extraLarge"
itemType="submit"
onClick={() => {
@@ -248,8 +248,7 @@ export const OnboardingPage = ({
setQuestionIdx(questionIdx + 1);
}
}}
iconPosition="end"
icon={<ArrowRightSmallIcon />}
suffix={<ArrowRightSmallIcon />}
>
{questionIdx === 0 ? 'start' : 'Next'}
</Button>
@@ -271,7 +270,7 @@ export const OnboardingPage = ({
</p>
<Button
className={clsx(styles.button, styles.openAFFiNEButton)}
type="primary"
variant="primary"
size="extraLarge"
onClick={() => {
if (callbackUrl) {
@@ -280,8 +279,7 @@ export const OnboardingPage = ({
onOpenAffine();
}
}}
iconPosition="end"
icon={<ArrowRightSmallIcon />}
suffix={<ArrowRightSmallIcon />}
>
Get Started
</Button>
@@ -59,7 +59,7 @@ export const SetPasswordPage: FC<{
}
>
{hasSetUp ? (
<Button type="primary" size="large" onClick={onOpenAffine}>
<Button variant="primary" size="large" onClick={onOpenAffine}>
{t['com.affine.auth.open.affine']()}
</Button>
) : (
@@ -33,7 +33,7 @@ export const SetPassword: FC<{
/>
</Wrapper>
<Button
type="primary"
variant="primary"
size="large"
disabled={!passwordPass}
style={{ marginRight: 20 }}
@@ -44,7 +44,7 @@ export const SetPassword: FC<{
{t['com.affine.auth.set.password.save']()}
</Button>
{showLater ? (
<Button type="plain" size="large" onClick={onLater}>
<Button variant="plain" size="large" onClick={onLater}>
{t['com.affine.auth.later']()}
</Button>
) : null}
@@ -13,7 +13,7 @@ export const SignInSuccessPage: FC<{
title={t['com.affine.auth.signed.success.title']()}
subtitle={t['com.affine.auth.signed.success.subtitle']()}
>
<Button type="primary" size="large" onClick={onOpenAffine}>
<Button variant="primary" size="large" onClick={onOpenAffine}>
{t['com.affine.auth.open.affine']()}
</Button>
</AuthPageContainer>
@@ -63,7 +63,7 @@ export const SignUpPage: FC<{
}
>
{hasSetUp ? (
<Button type="primary" size="large" onClick={onOpenAffine}>
<Button variant="primary" size="large" onClick={onOpenAffine}>
{openButtonText ?? t['com.affine.auth.open.affine']()}
</Button>
) : (
@@ -3,7 +3,6 @@ import { UNTITLED_WORKSPACE_NAME } from '@affine/env/constant';
import { WorkspaceFlavour } from '@affine/env/workspace';
import { CollaborationIcon, SettingsIcon } from '@blocksuite/icons/rc';
import type { WorkspaceMetadata } from '@toeverything/infra';
import clsx from 'clsx';
import { type MouseEvent, useCallback } from 'react';
import { Button } from '../../../ui/button';
@@ -89,8 +88,7 @@ export const WorkspaceCard = ({
<Button
loading={!!openingId && openingId === meta.id}
disabled={!!openingId}
type="default"
className={clsx(styles.enableCloudButton, styles.showOnCardHover)}
className={styles.showOnCardHover}
onClick={onEnableCloud}
>
{enableCloudText}
@@ -79,21 +79,16 @@ export const settingButton = style({
boxShadow: cssVar('shadow1'),
background: cssVar('white80'),
},
// [`.${card}:hover &:hover`]: {
// background: cssVar('hoverColor'),
// },
},
});
export const enableCloudButton = style({
background: 'transparent',
});
export const showOnCardHover = style({
display: 'none',
visibility: 'hidden',
opacity: 0,
selectors: {
[`.${card}:hover &`]: {
display: 'block',
visibility: 'visible',
opacity: 1,
},
},
});
@@ -13,7 +13,7 @@ export const PublicLinkDisableModal = (props: ConfirmModalProps) => {
cancelText={t['com.affine.publicLinkDisableModal.button.cancel']()}
confirmText={t['com.affine.publicLinkDisableModal.button.disable']()}
confirmButtonOptions={{
type: 'error',
variant: 'error',
['data-testid' as string]: 'confirm-enable-affine-cloud-button',
}}
{...props}
@@ -16,6 +16,9 @@ import {
importPageContainerStyle,
} from './index.css';
/**
* @deprecated Not used
*/
export const ImportPage = ({
importMarkdown,
importHtml,
@@ -37,8 +40,9 @@ export const ImportPage = ({
onClick={() => {
onClose();
}}
icon={<CloseIcon />}
/>
>
<CloseIcon />
</IconButton>
<div className={importPageBodyStyle}>
<div className="title">Import</div>
<span>
@@ -37,7 +37,7 @@ export const AcceptInvitePage = ({
</FlexWrapper>
}
>
<Button type="primary" size="large" onClick={onOpenWorkspace}>
<Button variant="primary" size="large" onClick={onOpenWorkspace}>
{t['Visit Workspace']()}
</Button>
</AuthPageContainer>
@@ -60,7 +60,7 @@ export const InviteModal = ({
confirmText={t['Invite']()}
confirmButtonOptions={{
loading: isMutating,
type: 'primary',
variant: 'primary',
['data-testid' as string]: 'confirm-enable-affine-cloud-button',
}}
onConfirm={handleConfirm}
@@ -44,7 +44,7 @@ export const MemberLimitModal = ({
: 'com.affine.payment.member-limit.pro.confirm'
]()}
confirmButtonOptions={{
type: 'primary',
variant: 'primary',
}}
onConfirm={handleConfirm}
></ConfirmModal>
@@ -3,7 +3,6 @@ import { SignOutIcon } from '@blocksuite/icons/rc';
import { Avatar } from '../../ui/avatar';
import { Button, IconButton } from '../../ui/button';
import { Tooltip } from '../../ui/tooltip';
import { AffineOtherPageLayout } from '../affine-other-page-layout';
import type { User } from '../auth-components';
import { NotFoundPattern } from './not-found-pattern';
@@ -38,7 +37,7 @@ export const NoPermissionOrNotFound = ({
<p className={wrapper}>{t['404.hint']()}</p>
<div className={wrapper}>
<Button
type="primary"
variant="primary"
size="extraLarge"
onClick={onBack}
className={largeButtonEffect}
@@ -49,11 +48,13 @@ export const NoPermissionOrNotFound = ({
<div className={wrapper}>
<Avatar url={user.avatar ?? user.image} name={user.label} />
<span style={{ margin: '0 12px' }}>{user.email}</span>
<Tooltip content={t['404.signOut']()}>
<IconButton onClick={onSignOut}>
<SignOutIcon />
</IconButton>
</Tooltip>
<IconButton
onClick={onSignOut}
size="20"
tooltip={t['404.signOut']()}
>
<SignOutIcon />
</IconButton>
</div>
</>
) : (
@@ -80,7 +81,7 @@ export const NotFoundPage = ({
<p className={wrapper}>{t['404.hint']()}</p>
<div className={wrapper}>
<Button
type="primary"
variant="primary"
size="extraLarge"
onClick={onBack}
className={largeButtonEffect}
@@ -93,11 +94,13 @@ export const NotFoundPage = ({
<div className={wrapper}>
<Avatar url={user.avatar ?? user.image} name={user.label} />
<span style={{ margin: '0 12px' }}>{user.email}</span>
<Tooltip content={t['404.signOut']()}>
<IconButton onClick={onSignOut}>
<SignOutIcon />
</IconButton>
</Tooltip>
<IconButton
onClick={onSignOut}
size="20"
tooltip={t['404.signOut']()}
>
<SignOutIcon />
</IconButton>
</div>
) : null}
</div>