From 3d855647c7d9e492824c8edef112b76dc709b2b6 Mon Sep 17 00:00:00 2001
From: CatsJuice
Date: Mon, 5 Aug 2024 02:57:23 +0000
Subject: [PATCH] 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 ``
- 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

---
.../affine-banner/local-demo-tips.tsx | 8 +-
.../mobile-navbar.tsx | 2 +-
.../auth-components/back-button.tsx | 7 +-
.../auth-components/change-email-page.tsx | 2 +-
.../auth-components/change-password-page.tsx | 2 +-
.../auth-components/confirm-change-email.tsx | 2 +-
.../auth-components/email-verified-email.tsx | 2 +-
.../auth-components/onboarding-page.tsx | 10 +-
.../auth-components/set-password-page.tsx | 2 +-
.../auth-components/set-password.tsx | 4 +-
.../auth-components/sign-in-success-page.tsx | 2 +-
.../auth-components/sign-up-page.tsx | 2 +-
.../components/card/workspace-card/index.tsx | 4 +-
.../card/workspace-card/styles.css.ts | 13 +-
.../components/disable-public-link/index.tsx | 2 +-
.../src/components/import-page/index.tsx | 8 +-
.../member-components/accept-invite-page.tsx | 2 +-
.../member-components/invite-modal.tsx | 2 +-
.../member-components/member-limit-modal.tsx | 2 +-
.../not-found-page/not-found-page.tsx | 29 +-
.../component/src/ui/avatar/avatar.tsx | 27 +-
.../component/src/ui/avatar/style.css.ts | 14 +-
.../component/src/ui/button/button.css.ts | 578 +++++++-----------
.../src/ui/button/button.stories.css.ts | 61 ++
.../src/ui/button/button.stories.tsx | 194 +++++-
.../component/src/ui/button/button.tsx | 266 ++++----
.../src/ui/button/icon-button.stories.tsx | 172 ++++--
.../component/src/ui/button/icon-button.tsx | 135 ++--
.../component/src/ui/button/interface.ts | 26 -
.../frontend/component/src/ui/button/utils.ts | 89 ---
.../src/ui/date-picker/calendar/items.tsx | 6 +-
.../component/src/ui/loading/loading.tsx | 9 +-
.../component/src/ui/modal/modal.stories.tsx | 4 +-
.../frontend/component/src/ui/modal/modal.tsx | 10 +-
.../admin-panel/admin-panel-header.tsx | 4 +-
.../error-basic/error-detail.tsx | 2 +-
.../ai-onboarding/edgeless.dialog.css.ts | 1 -
.../affine/ai-onboarding/edgeless.dialog.tsx | 4 +-
.../ai-onboarding/general.dialog.css.ts | 14 -
.../affine/ai-onboarding/general.dialog.tsx | 41 +-
.../affine/ai-onboarding/local.dialog.tsx | 4 +-
.../affine/auth/after-sign-in-send-email.tsx | 2 +-
.../affine/auth/after-sign-up-send-email.tsx | 2 +-
.../affine/auth/ai-login-required.tsx | 2 +-
.../core/src/components/affine/auth/oauth.tsx | 6 +-
.../src/components/affine/auth/send-email.tsx | 2 +-
.../affine/auth/sign-in-with-password.tsx | 2 +-
.../src/components/affine/auth/sign-in.tsx | 17 +-
.../affine/create-workspace-modal/index.tsx | 2 +-
.../affine/history-tips-modal/index.tsx | 2 +-
.../affine/issue-feedback-modal/index.tsx | 2 +-
.../affine/onboarding/animate-in-tooltip.tsx | 2 +-
.../onboarding/steps/edgeless-switch.tsx | 4 +-
.../page-history-modal/history-modal.tsx | 16 +-
.../confirm-delete-property-modal.tsx | 2 +-
.../affine/page-properties/styles.css.ts | 12 +-
.../affine/page-properties/table.tsx | 27 +-
.../page-properties/tags-inline-editor.tsx | 8 +-
.../quota-reached-modal/cloud-quota-modal.tsx | 2 +-
.../quota-reached-modal/local-quota-modal.tsx | 2 +-
.../account-setting/ai-usage-panel.tsx | 2 +-
.../setting-modal/account-setting/index.tsx | 5 +-
.../account-setting/storage-progress.css.ts | 3 -
.../account-setting/storage-progress.tsx | 8 +-
.../account-setting/style.css.ts | 3 -
.../general-setting/billing/index.tsx | 22 +-
.../general-setting/billing/style.css.ts | 3 -
.../experimental-features/index.tsx | 2 +-
.../plans/ai/actions/cancel.tsx | 11 +-
.../plans/ai/actions/login.tsx | 2 +-
.../plans/ai/actions/resume.tsx | 9 +-
.../plans/ai/actions/subscribe.tsx | 2 +-
.../general-setting/plans/layout.css.ts | 1 +
.../general-setting/plans/layout.tsx | 4 +-
.../general-setting/plans/modals.tsx | 4 +-
.../general-setting/plans/plan-card.tsx | 34 +-
.../general-setting/plans/style.css.ts | 12 +-
.../delete-leave-workspace/delete/index.tsx | 2 +-
.../delete-leave-workspace/index.tsx | 2 +-
.../enable-cloud.tsx | 2 +-
.../new-workspace-setting-detail/members.tsx | 3 +-
.../workspace-setting/properties/index.tsx | 12 +-
.../share-menu/share-menu.tsx | 2 +-
.../share-menu/share-page.tsx | 4 +-
.../affine/sign-out-modal/index.tsx | 2 +-
.../affine/star-affine-modal/index.tsx | 2 +-
.../affine/subscription-landing/index.tsx | 2 +-
.../affine/subscription-landing/notify.tsx | 4 +-
.../app-sidebar/add-page-button/index.css.ts | 8 +-
.../app-sidebar/add-page-button/index.tsx | 23 +-
.../app-sidebar/category-divider/index.css.ts | 15 +-
.../app-sidebar/category-divider/index.tsx | 13 +-
.../sidebar-header/sidebar-switch.css.ts | 17 +-
.../sidebar-header/sidebar-switch.tsx | 21 +-
.../specs/custom/spec-patchers.tsx | 4 +-
.../block-suite-header/info/index.tsx | 17 +-
.../present/detail-header-present-button.tsx | 7 +-
.../cloud/share-header-right-item/present.tsx | 3 +-
.../share-header-right-item/styles.css.ts | 1 -
.../page-list/components/favorite-tag.tsx | 45 +-
.../components/page-display-menu.css.ts | 6 -
.../components/page-display-menu.tsx | 3 +-
.../page-list/docs/page-list-header.css.ts | 8 -
.../page-list/docs/page-list-header.tsx | 8 +-
.../page-list/filter/filter-list.tsx | 8 +-
.../core/src/components/page-list/list.css.ts | 10 +-
.../components/page-list/operation-cell.tsx | 95 +--
.../operation-menu-items/move-to-trash.tsx | 2 +-
.../page-list/selector/selector-layout.tsx | 8 +-
.../components/page-list/tags/create-tag.tsx | 2 +-
.../page-list/view/collection-list.css.ts | 3 -
.../page-list/view/collection-list.tsx | 3 +-
.../page-list/view/create-collection.tsx | 2 +-
.../edit-collection/edit-collection.css.ts | 14 +-
.../view/edit-collection/edit-collection.tsx | 8 +-
.../view/edit-collection/rules-mode.tsx | 37 +-
.../view/edit-collection/select-page.tsx | 19 +-
.../view/save-as-collection-button.tsx | 2 +-
.../page-list/virtualized-trash-list.tsx | 2 +-
.../pure/header-drop-down-button/index.tsx | 2 -
.../header-drop-down-button/styles.css.ts | 2 +-
.../pure/trash-page-footer/index.tsx | 44 +-
.../pure/trash-page-footer/styles.css.ts | 6 +-
.../components/root-app-sidebar/index.css.ts | 12 -
.../root-app-sidebar/trash-button.tsx | 2 +-
.../components/root-app-sidebar/user-info.tsx | 20 +-
.../workspace-upgrade/upgrade.css.ts | 16 +-
.../components/workspace-upgrade/upgrade.tsx | 13 +-
.../src/hooks/affine/use-enable-cloud.tsx | 2 +-
.../app-tabs-header/views/app-tabs-header.tsx | 12 +-
.../views/nodes/collection/operations.tsx | 8 +-
.../explorer/views/nodes/doc/operations.tsx | 2 +-
.../explorer/views/nodes/folder/index.tsx | 2 +-
.../explorer/views/nodes/tag/operations.tsx | 2 +-
.../views/sections/collections/index.tsx | 2 +-
.../views/sections/favorites/index.tsx | 2 +-
.../sections/migration-favorites/index.tsx | 11 +-
.../views/sections/old-favorites/index.tsx | 2 +-
.../views/sections/organize/index.tsx | 2 +-
.../explorer/views/sections/tags/index.tsx | 2 +-
.../src/modules/explorer/views/tree/node.tsx | 3 +-
.../view/find-in-page-modal.css.ts | 26 +-
.../find-in-page/view/find-in-page-modal.tsx | 97 ++-
.../navigation/view/navigation-buttons.tsx | 47 +-
.../peek-view/view/image-preview/index.tsx | 48 +-
.../peek-view/view/peek-view-controls.css.ts | 10 +-
.../peek-view/view/peek-view-controls.tsx | 24 +-
.../src/modules/tag/view/delete-tag-modal.tsx | 2 +-
.../workbench/view/route-container.tsx | 2 +-
.../view/sidebar/sidebar-header-switcher.tsx | 4 +-
.../workbench/view/sidebar/sidebar-header.tsx | 2 +-
packages/frontend/core/src/pages/expired.tsx | 2 +-
.../frontend/core/src/pages/subscribe.tsx | 2 +-
.../workspace/all-collection/header.css.ts | 8 +-
.../pages/workspace/all-collection/header.tsx | 4 +-
.../workspace/collection/collection.css.ts | 6 +-
.../src/pages/workspace/collection/header.tsx | 4 +-
.../workspace/detail-page/tabs/journal.tsx | 2 +-
tests/affine-local/e2e/quick-search.spec.ts | 4 +-
159 files changed, 1384 insertions(+), 1539 deletions(-)
create mode 100644 packages/frontend/component/src/ui/button/button.stories.css.ts
delete mode 100644 packages/frontend/component/src/ui/button/interface.ts
delete mode 100644 packages/frontend/component/src/ui/button/utils.ts
diff --git a/packages/frontend/component/src/components/affine-banner/local-demo-tips.tsx b/packages/frontend/component/src/components/affine-banner/local-demo-tips.tsx
index b42be3727c..6a81eefa0a 100644
--- a/packages/frontend/component/src/components/affine-banner/local-demo-tips.tsx
+++ b/packages/frontend/component/src/components/affine-banner/local-demo-tips.tsx
@@ -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 = ({
-
-
-
+
diff --git a/packages/frontend/component/src/components/affine-other-page-layout/mobile-navbar.tsx b/packages/frontend/component/src/components/affine-other-page-layout/mobile-navbar.tsx
index c3d3d43aa7..d0a79ae482 100644
--- a/packages/frontend/component/src/components/affine-other-page-layout/mobile-navbar.tsx
+++ b/packages/frontend/component/src/components/affine-other-page-layout/mobile-navbar.tsx
@@ -41,7 +41,7 @@ export const MobileNavbar = () => {
onOpenChange: setOpenMenu,
}}
>
-
+
{openMenu ? : }
diff --git a/packages/frontend/component/src/components/auth-components/back-button.tsx b/packages/frontend/component/src/components/auth-components/back-button.tsx
index eade042a77..fa886dd8cb 100644
--- a/packages/frontend/component/src/components/auth-components/back-button.tsx
+++ b/packages/frontend/component/src/components/auth-components/back-button.tsx
@@ -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 = props => {
const t = useI18n();
return (
}
+ prefix={}
{...props}
>
{t['com.affine.backButton']()}
diff --git a/packages/frontend/component/src/components/auth-components/change-email-page.tsx b/packages/frontend/component/src/components/auth-components/change-email-page.tsx
index d2dbaa75f1..3043a1deb5 100644
--- a/packages/frontend/component/src/components/auth-components/change-email-page.tsx
+++ b/packages/frontend/component/src/components/auth-components/change-email-page.tsx
@@ -58,7 +58,7 @@ export const ChangeEmailPage = ({
disabled={hasSetUp}
/>
Import
diff --git a/packages/frontend/component/src/components/member-components/accept-invite-page.tsx b/packages/frontend/component/src/components/member-components/accept-invite-page.tsx
index 1005ab44f1..d6d08bfecb 100644
--- a/packages/frontend/component/src/components/member-components/accept-invite-page.tsx
+++ b/packages/frontend/component/src/components/member-components/accept-invite-page.tsx
@@ -37,7 +37,7 @@ export const AcceptInvitePage = ({
}
>
-
+
{t['Visit Workspace']()}
diff --git a/packages/frontend/component/src/components/member-components/invite-modal.tsx b/packages/frontend/component/src/components/member-components/invite-modal.tsx
index 2d4d950611..be4eb92a7e 100644
--- a/packages/frontend/component/src/components/member-components/invite-modal.tsx
+++ b/packages/frontend/component/src/components/member-components/invite-modal.tsx
@@ -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}
diff --git a/packages/frontend/component/src/components/member-components/member-limit-modal.tsx b/packages/frontend/component/src/components/member-components/member-limit-modal.tsx
index f38a9e05e3..76f3470e5b 100644
--- a/packages/frontend/component/src/components/member-components/member-limit-modal.tsx
+++ b/packages/frontend/component/src/components/member-components/member-limit-modal.tsx
@@ -44,7 +44,7 @@ export const MemberLimitModal = ({
: 'com.affine.payment.member-limit.pro.confirm'
]()}
confirmButtonOptions={{
- type: 'primary',
+ variant: 'primary',
}}
onConfirm={handleConfirm}
>
diff --git a/packages/frontend/component/src/components/not-found-page/not-found-page.tsx b/packages/frontend/component/src/components/not-found-page/not-found-page.tsx
index 7c30372b46..63311da9b3 100644
--- a/packages/frontend/component/src/components/not-found-page/not-found-page.tsx
+++ b/packages/frontend/component/src/components/not-found-page/not-found-page.tsx
@@ -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 = ({
{t['404.hint']()}
{user.email}
-
-
-
-
-
+
+
+
>
) : (
@@ -80,7 +81,7 @@ export const NotFoundPage = ({
{t['404.hint']()}
{user.email}
-
-
-
-
-
+
+
+
) : null}
diff --git a/packages/frontend/component/src/ui/avatar/avatar.tsx b/packages/frontend/component/src/ui/avatar/avatar.tsx
index b5a9be8317..f59a044dc0 100644
--- a/packages/frontend/component/src/ui/avatar/avatar.tsx
+++ b/packages/frontend/component/src/ui/avatar/avatar.tsx
@@ -199,21 +199,20 @@ export const Avatar = forwardRef(
{onRemove ? (
-
-
-
-
-
+
+
) : null}
);
diff --git a/packages/frontend/component/src/ui/avatar/style.css.ts b/packages/frontend/component/src/ui/avatar/style.css.ts
index a7def125e9..da67380c2b 100644
--- a/packages/frontend/component/src/ui/avatar/style.css.ts
+++ b/packages/frontend/component/src/ui/avatar/style.css.ts
@@ -1,5 +1,5 @@
import { cssVar } from '@toeverything/theme';
-import { createVar, globalStyle, keyframes, style } from '@vanilla-extract/css';
+import { createVar, keyframes, style } from '@vanilla-extract/css';
export const sizeVar = createVar('sizeVar');
export const blurVar = createVar('blurVar');
const bottomAnimation = keyframes({
@@ -172,7 +172,7 @@ export const hoverWrapper = style({
alignItems: 'center',
backgroundColor: 'rgba(60, 61, 63, 0.5)',
zIndex: '1',
- color: cssVar('white'),
+ color: cssVar('pureWhite'),
opacity: 0,
transition: 'opacity .15s',
cursor: 'pointer',
@@ -189,14 +189,8 @@ export const removeButton = style({
visibility: 'hidden',
zIndex: '1',
selectors: {
- '&:hover': {
- background: '#f6f6f6',
+ [`${avatarRoot}:hover &`]: {
+ visibility: 'visible',
},
},
});
-globalStyle(`${avatarRoot}:hover ${removeButton}`, {
- visibility: 'visible',
-});
-globalStyle(`${avatarRoot} ${removeButton}:hover`, {
- background: '#f6f6f6',
-});
diff --git a/packages/frontend/component/src/ui/button/button.css.ts b/packages/frontend/component/src/ui/button/button.css.ts
index 72c0a0859a..31126a7d55 100644
--- a/packages/frontend/component/src/ui/button/button.css.ts
+++ b/packages/frontend/component/src/ui/button/button.css.ts
@@ -1,371 +1,259 @@
import { cssVar } from '@toeverything/theme';
-import { globalStyle, style } from '@vanilla-extract/css';
+import { cssVarV2 } from '@toeverything/theme/v2';
+import { createVar, globalStyle, style } from '@vanilla-extract/css';
+
+// Using variables can override externally, without considering the priority of selectors.
+// size vars
+export const hVar = createVar('height');
+export const wVar = createVar('width');
+export const iconSizeVar = createVar('iconSize');
+const gapVar = createVar('gap');
+const paddingVar = createVar('padding');
+const fontSizeVar = createVar('fontSize');
+const fontWeightVar = createVar('fontWeight');
+const lineHeightVar = createVar('lineHeight');
+const shadowVar = createVar('shadow');
+
+// style vars
+const bgVar = createVar('bg');
+const textVar = createVar('fg');
+const iconColorVar = createVar('icon');
+const borderColorVar = createVar('border');
+const borderWidthVar = createVar('borderWidth');
+
export const button = style({
- display: 'inline-flex',
- justifyContent: 'center',
- alignItems: 'center',
- userSelect: 'none',
- touchAction: 'manipulation',
+ vars: {
+ // default vars
+ [gapVar]: '4px',
+ [wVar]: 'unset',
+ [hVar]: 'unset',
+ [borderWidthVar]: '1px',
+ },
+
flexShrink: 0,
- outline: '0',
- border: '1px solid',
- padding: '0 8px',
- borderRadius: '8px',
- fontSize: cssVar('fontXs'),
- fontWeight: 500,
+ position: 'relative',
+ display: 'inline-flex',
+ alignItems: 'center',
+ justifyContent: 'center',
+ userSelect: 'none',
+ outline: 0,
+ borderRadius: 8,
transition: 'all .3s',
- ['WebkitAppRegion' as string]: 'no-drag',
cursor: 'pointer',
- // changeable
- height: '28px',
- background: cssVar('white'),
- borderColor: cssVar('borderColor'),
- color: cssVar('textPrimaryColor'),
+ ['WebkitAppRegion' as string]: 'no-drag',
+
+ // hover layer
+ ':before': {
+ content: '""',
+ position: 'absolute',
+ width: '100%',
+ height: '100%',
+ transition: 'inherit',
+ borderRadius: 'inherit',
+ opacity: 0,
+ left: '50%',
+ top: '50%',
+ transform: 'translate(-50%, -50%)',
+ backgroundColor: cssVarV2('layer/background/hoverOverlay'),
+ borderColor: 'transparent',
+ pointerEvents: 'none',
+ borderWidth: 'inherit',
+ borderStyle: 'inherit',
+ },
+
+ // style
+ backgroundColor: bgVar,
+ color: textVar,
+ boxShadow: shadowVar,
+ borderWidth: borderWidthVar,
+ borderStyle: 'solid',
+ borderColor: borderColorVar,
+
+ // size
+ width: wVar,
+ height: hVar,
+ gap: gapVar,
+ padding: paddingVar,
+ fontSize: fontSizeVar,
+ fontWeight: fontWeightVar,
+ lineHeight: lineHeightVar,
+
selectors: {
- '&.text-bold': {
- fontWeight: 600,
- },
- '&:not(.without-hover):hover': {
- background: cssVar('hoverColor'),
- },
- '&.disabled': {
- opacity: '.4',
- cursor: 'default',
- color: cssVar('textDisableColor'),
- pointerEvents: 'none',
- },
- '&.loading': {
- cursor: 'default',
- color: cssVar('textDisableColor'),
- pointerEvents: 'none',
- },
- '&.disabled:not(.without-hover):hover, &.loading:not(.without-hover):hover':
- {
- background: 'inherit',
+ '&:hover:before': { opacity: 1 },
+ '&[data-block]': { display: 'flex' },
+
+ // size
+ '&[data-size="default"]': {
+ vars: {
+ [hVar]: '28px', // line-height + paddingY * 2 (to ignore border width)
+ [paddingVar]: '0px 8px',
+ [iconSizeVar]: '16px',
+ [paddingVar]: '4px 12px',
+ [fontSizeVar]: cssVar('fontXs'),
+ [fontWeightVar]: '500',
+ [lineHeightVar]: '20px',
},
- '&.block': {
- display: 'flex',
+ },
+ '&[data-size="large"]': {
+ vars: {
+ [hVar]: '32px',
+ [paddingVar]: '0px 8px',
+ [iconSizeVar]: '20px',
+ [paddingVar]: '4px 12px',
+ [fontSizeVar]: '15px',
+ [fontWeightVar]: '500',
+ [lineHeightVar]: '24px',
+ },
+ },
+ '&[data-size="extraLarge"]': {
+ vars: {
+ [hVar]: '40px',
+ [paddingVar]: '0px 8px',
+ [iconSizeVar]: '24px',
+ [paddingVar]: '8px 18px',
+ [fontSizeVar]: '15',
+ [fontWeightVar]: '600',
+ [lineHeightVar]: '24px',
+ },
+ },
+
+ // type
+ '&[data-variant="primary"]': {
+ vars: {
+ [bgVar]: cssVarV2('button/primary'),
+ [textVar]: cssVarV2('button/pureWhiteText'),
+ [iconColorVar]: cssVarV2('button/pureWhiteText'),
+ [borderColorVar]: cssVarV2('button/innerBlackBorder'),
+ },
+ },
+ '&[data-variant="secondary"]': {
+ vars: {
+ [bgVar]: cssVarV2('button/secondary'),
+ [textVar]: cssVarV2('text/primary'),
+ [iconColorVar]: cssVarV2('icon/primary'),
+ [borderColorVar]: cssVarV2('layer/border'),
+ },
+ },
+ '&[data-variant="plain"]': {
+ vars: {
+ [bgVar]: 'transparent',
+ [textVar]: cssVarV2('text/primary'),
+ [iconColorVar]: cssVarV2('icon/primary'),
+ [borderColorVar]: 'transparent',
+ [borderWidthVar]: '0px',
+ },
+ },
+ '&[data-variant="error"]': {
+ vars: {
+ [bgVar]: cssVarV2('button/error'),
+ [textVar]: cssVarV2('button/pureWhiteText'),
+ [iconColorVar]: cssVarV2('button/pureWhiteText'),
+ [borderColorVar]: cssVarV2('button/innerBlackBorder'),
+ },
+ },
+ '&[data-variant="success"]': {
+ vars: {
+ [bgVar]: cssVarV2('button/success'),
+ [textVar]: cssVarV2('button/pureWhiteText'),
+ [iconColorVar]: cssVarV2('button/pureWhiteText'),
+ [borderColorVar]: cssVarV2('button/innerBlackBorder'),
+ },
+ },
+
+ // disabled
+ '&[data-disabled]': {
+ cursor: 'not-allowed',
+ opacity: 0.5,
+ },
+
+ // default keyboard focus style
+ '&:focus-visible::after': {
+ content: '""',
width: '100%',
- },
- '&.circle': {
- borderRadius: '50%',
- },
- '&.round': {
- borderRadius: '14px',
- },
- // size
- '&.large': {
- height: '32px',
- fontSize: cssVar('fontBase'),
- fontWeight: 600,
- },
- '&.round.large': {
- borderRadius: '16px',
- },
- '&.extraLarge': {
- height: '40px',
- fontSize: cssVar('fontBase'),
- fontWeight: 700,
- },
- '&.extraLarge.primary': {
- boxShadow: `${cssVar('largeButtonEffect')} !important`,
- },
- '&.round.extraLarge': {
- borderRadius: '20px',
- },
- // type
- '&.plain': {
- color: cssVar('textPrimaryColor'),
- borderColor: 'transparent',
- background: 'transparent',
- },
- '&.primary': {
- color: cssVar('pureWhite'),
- background: cssVar('primaryColor'),
- borderColor: cssVar('black10'),
- },
- '&.primary:not(.without-hover):hover': {
- background: `linear-gradient(0deg, rgba(0, 0, 0, 0.04) 0%, rgba(0, 0, 0, 0.04) 100%), ${cssVar(
- 'primaryColor'
- )}`,
- },
- '&.primary.disabled': {
- opacity: '.4',
- cursor: 'default',
- },
- '&.primary.disabled:not(.without-hover):hover': {
- background: cssVar('primaryColor'),
- },
- '&.error': {
- color: cssVar('pureWhite'),
- background: cssVar('errorColor'),
- borderColor: cssVar('black10'),
- },
- '&.error:not(.without-hover):hover': {
- background: `linear-gradient(0deg, rgba(0, 0, 0, 0.04) 0%, rgba(0, 0, 0, 0.04) 100%), ${cssVar(
- 'errorColor'
- )}`,
- },
- '&.error.disabled': {
- opacity: '.4',
- cursor: 'default',
- },
- '&.error.disabled:not(.without-hover):hover': {
- background: cssVar('errorColor'),
- },
- '&.warning': {
- color: cssVar('pureWhite'),
- background: cssVar('warningColor'),
- borderColor: cssVar('black10'),
- },
- '&.warning:not(.without-hover):hover': {
- background: `linear-gradient(0deg, rgba(0, 0, 0, 0.04) 0%, rgba(0, 0, 0, 0.04) 100%), ${cssVar(
- 'warningColor'
- )}`,
- },
- '&.warning.disabled': {
- opacity: '.4',
- cursor: 'default',
- },
- '&.warning.disabled:not(.without-hover):hover': {
- background: cssVar('warningColor'),
- },
- '&.success': {
- color: cssVar('pureWhite'),
- background: cssVar('successColor'),
- borderColor: cssVar('black10'),
- },
- '&.success:not(.without-hover):hover': {
- background: `linear-gradient(0deg, rgba(0, 0, 0, 0.04) 0%, rgba(0, 0, 0, 0.04) 100%), ${cssVar(
- 'successColor'
- )}`,
- },
- '&.success.disabled': {
- opacity: '.4',
- cursor: 'default',
- },
- '&.success.disabled:not(.without-hover):hover': {
- background: cssVar('successColor'),
- },
- '&.processing': {
- color: cssVar('pureWhite'),
- background: cssVar('processingColor'),
- borderColor: cssVar('black10'),
- },
- '&.processing:not(.without-hover):hover': {
- background: `linear-gradient(0deg, rgba(0, 0, 0, 0.04) 0%, rgba(0, 0, 0, 0.04) 100%), ${cssVar(
- 'processingColor'
- )}`,
- },
- '&.processing.disabled': {
- opacity: '.4',
- cursor: 'default',
- },
- '&.processing.disabled:not(.without-hover):hover': {
- background: cssVar('processingColor'),
- },
- '&.danger:hover': {
- color: cssVar('errorColor'),
- background: cssVar('backgroundErrorColor'),
+ height: '100%',
+ position: 'absolute',
+ top: 0,
+ left: 0,
+ borderRadius: 'inherit',
+ boxShadow: `0 0 0 1px ${cssVarV2('layer/insideBorder/primary')}`,
},
},
});
-globalStyle(`${button} > span`, {
- // flex: 1,
- lineHeight: 1,
- padding: '0 4px',
+export const content = style({
+ // in case that width is specified by parent and text is too long
+ textOverflow: 'ellipsis',
+ whiteSpace: 'nowrap',
+ overflow: 'hidden',
});
-export const buttonIcon = style({
+
+export const icon = style({
flexShrink: 0,
- display: 'inline-flex',
- justifyContent: 'center',
- alignItems: 'center',
- color: cssVar('iconColor'),
- fontSize: '16px',
- width: '16px',
- height: '16px',
- selectors: {
- '&.start': {
- marginRight: '4px',
- },
- '&.end': {
- marginLeft: '4px',
- },
- '&.large': {
- fontSize: '20px',
- width: '20px',
- height: '20px',
- },
- '&.extraLarge': {
- fontSize: '20px',
- width: '20px',
- height: '20px',
- },
- '&.color-white': {
- color: cssVar('pureWhite'),
- },
- },
+ // There are two kinds of icon size:
+ // 1. control by props: width and height
+ width: iconSizeVar,
+ height: iconSizeVar,
+ // 2. width/height is set to `1em`
+ fontSize: iconSizeVar,
+ color: iconColorVar,
});
+globalStyle(`${icon} > svg`, {
+ width: '100%',
+ height: '100%',
+ display: 'block',
+});
+
export const iconButton = style({
- display: 'inline-flex',
- justifyContent: 'center',
- alignItems: 'center',
- userSelect: 'none',
- touchAction: 'manipulation',
- outline: '0',
- border: '1px solid',
- borderRadius: '4px',
- transition: 'all .3s',
- ['WebkitAppRegion' as string]: 'no-drag',
- cursor: 'pointer',
- background: cssVar('white'),
- // changeable
- width: '24px',
- height: '24px',
- fontSize: '20px',
- color: cssVar('textPrimaryColor'),
- borderColor: cssVar('borderColor'),
+ vars: {
+ [paddingVar]: '2px',
+ // TODO(@CatsJuice): Replace with theme variables when ready
+ '--shadow':
+ '0px 0px 1px 0px rgba(0, 0, 0, 0.12), 0px 1px 5px 0px rgba(0, 0, 0, 0.12)',
+ },
+ borderRadius: 4,
selectors: {
- '&.without-padding': {
- margin: '-2px',
- },
- '&.active': {
- color: cssVar('primaryColor'),
- },
- '&:not(.without-hover):hover': {
- background: cssVar('hoverColor'),
- },
- '&.disabled': {
- opacity: '.4',
- cursor: 'default',
- color: cssVar('textDisableColor'),
- pointerEvents: 'none',
- },
- '&.loading': {
- cursor: 'default',
- color: cssVar('textDisableColor'),
- pointerEvents: 'none',
- },
- '&.disabled:not(.without-hover):hover, &.loading:not(.without-hover):hover':
- {
- background: 'inherit',
+ '[data-theme="dark"] &': {
+ vars: {
+ '--shadow':
+ '0px 0px 1px 0px rgba(0, 0, 0, 0.66), 0px 1px 5px 0px rgba(0, 0, 0, 0.72)',
},
- // size
- '&.large': {
- width: '32px',
- height: '32px',
- fontSize: '24px',
},
- '&.large.without-padding': {
- margin: '-4px',
+ '&[data-icon-variant="plain"]': {
+ vars: {
+ [bgVar]: 'transparent',
+ [iconColorVar]: cssVarV2('icon/primary'),
+ [borderColorVar]: 'transparent',
+ [borderWidthVar]: '0px',
+ },
},
- '&.small': {
- width: '20px',
- height: '20px',
- fontSize: '16px',
+ '&[data-icon-variant="danger"]': {
+ vars: {
+ [bgVar]: 'transparent',
+ [iconColorVar]: cssVarV2('icon/primary'),
+ [borderColorVar]: 'transparent',
+ [borderWidthVar]: '0px',
+ },
},
- '&.extra-small': {
- width: '16px',
- height: '16px',
- fontSize: '12px',
+ '&[data-icon-variant="danger"]:hover': {
+ vars: {
+ [bgVar]: cssVar('backgroundErrorColor'),
+ [iconColorVar]: cssVar('errorColor'),
+ },
},
- // type
- '&.plain': {
- color: cssVar('iconColor'),
- borderColor: 'transparent',
- background: 'transparent',
+ // disable hover layer for danger type
+ '&[data-icon-variant="danger"]:hover:before': {
+ opacity: 0,
},
- '&.plain.active': {
- color: cssVar('primaryColor'),
+ '&[data-icon-variant="solid"]': {
+ vars: {
+ [bgVar]: cssVarV2('button/iconButtonSolid'),
+ [iconColorVar]: cssVarV2('icon/primary'),
+ [borderColorVar]: 'transparent',
+ [shadowVar]: 'var(--shadow)',
+ },
},
- '&.primary': {
- color: cssVar('white'),
- background: cssVar('primaryColor'),
- borderColor: cssVar('black10'),
- },
- '&.primary:not(.without-hover):hover': {
- background: `linear-gradient(0deg, rgba(0, 0, 0, 0.04) 0%, rgba(0, 0, 0, 0.04) 100%), ${cssVar(
- 'primaryColor'
- )}`,
- },
- '&.primary.disabled': {
- opacity: '.4',
- cursor: 'default',
- },
- '&.primary.disabled:not(.without-hover):hover': {
- background: cssVar('primaryColor'),
- },
- '&.error': {
- color: cssVar('white'),
- background: cssVar('errorColor'),
- borderColor: cssVar('black10'),
- },
- '&.error:not(.without-hover):hover': {
- background: `linear-gradient(0deg, rgba(0, 0, 0, 0.04) 0%, rgba(0, 0, 0, 0.04) 100%), ${cssVar(
- 'errorColor'
- )}`,
- },
- '&.error.disabled': {
- opacity: '.4',
- cursor: 'default',
- },
- '&.error.disabled:not(.without-hover):hover': {
- background: cssVar('errorColor'),
- },
- '&.warning': {
- color: cssVar('white'),
- background: cssVar('warningColor'),
- borderColor: cssVar('black10'),
- },
- '&.warning:not(.without-hover):hover': {
- background: `linear-gradient(0deg, rgba(0, 0, 0, 0.04) 0%, rgba(0, 0, 0, 0.04) 100%), ${cssVar(
- 'warningColor'
- )}`,
- },
- '&.warning.disabled': {
- opacity: '.4',
- cursor: 'default',
- },
- '&.warning.disabled:not(.without-hover):hover': {
- background: cssVar('warningColor'),
- },
- '&.success': {
- color: cssVar('white'),
- background: cssVar('successColor'),
- borderColor: cssVar('black10'),
- },
- '&.success:not(.without-hover):hover': {
- background: `linear-gradient(0deg, rgba(0, 0, 0, 0.04) 0%, rgba(0, 0, 0, 0.04) 100%), ${cssVar(
- 'successColor'
- )}`,
- },
- '&.success.disabled': {
- opacity: '.4',
- cursor: 'default',
- },
- '&.success.disabled:not(.without-hover):hover': {
- background: cssVar('successColor'),
- },
- '&.processing': {
- color: cssVar('white'),
- background: cssVar('processingColor'),
- borderColor: cssVar('black10'),
- },
- '&.processing:not(.without-hover):hover': {
- background: `linear-gradient(0deg, rgba(0, 0, 0, 0.04) 0%, rgba(0, 0, 0, 0.04) 100%), ${cssVar(
- 'processingColor'
- )}`,
- },
- '&.processing.disabled': {
- opacity: '.4',
- cursor: 'default',
- },
- '&.processing.disabled:not(.without-hover):hover': {
- background: cssVar('processingColor'),
- },
- '&.danger:hover': {
- color: cssVar('errorColor'),
- background: cssVar('backgroundErrorColor'),
+
+ '&[data-icon-size="24"]': {
+ vars: { [paddingVar]: '4px' },
},
},
});
diff --git a/packages/frontend/component/src/ui/button/button.stories.css.ts b/packages/frontend/component/src/ui/button/button.stories.css.ts
new file mode 100644
index 0000000000..c73cc52dc4
--- /dev/null
+++ b/packages/frontend/component/src/ui/button/button.stories.css.ts
@@ -0,0 +1,61 @@
+import { globalStyle, style } from '@vanilla-extract/css';
+
+// table
+export const table = style({
+ vars: { '--border-color': '#974FFF' },
+});
+globalStyle(`${table} thead td, ${table} tbody tr td:nth-child(1)`, {
+ backgroundColor: '#974FFF10',
+ padding: '16px',
+ fontWeight: 600,
+ fontSize: 12,
+ color: 'var(--border-color)',
+});
+globalStyle(`${table} td`, {
+ textAlign: 'center',
+ border: '0.5px dashed var(--border-color)',
+ borderTopColor: 'transparent',
+ borderBottomColor: 'transparent',
+ padding: '16px 8px',
+});
+globalStyle(`${table} thead td`, {
+ borderTopColor: 'var(--border-color)',
+});
+globalStyle(`${table} tbody tr:last-child td`, {
+ borderBottomColor: 'var(--border-color)',
+});
+
+export const settings = style({
+ display: 'flex',
+ flexWrap: 'wrap',
+ gap: '8px 100px',
+ marginBottom: 40,
+});
+globalStyle(`${settings} > section`, {
+ display: 'flex',
+ alignItems: 'center',
+});
+globalStyle(`${settings} > section > span`, {
+ display: 'inline-block',
+ width: 200,
+});
+
+export const overrideBackground = style({
+ background: 'cyan',
+});
+export const overrideTextColor = style({
+ color: 'red',
+});
+export const overrideBorder = style({
+ borderColor: 'green',
+});
+export const overrideFontSize = style({
+ fontSize: 24,
+});
+export const overrideIconSize = style({
+ width: 60,
+ height: 60,
+});
+export const overrideIconColor = style({
+ color: 'forestgreen',
+});
diff --git a/packages/frontend/component/src/ui/button/button.stories.tsx b/packages/frontend/component/src/ui/button/button.stories.tsx
index 44c218b8ba..933d90a411 100644
--- a/packages/frontend/component/src/ui/button/button.stories.tsx
+++ b/packages/frontend/component/src/ui/button/button.stories.tsx
@@ -1,47 +1,183 @@
-import { InformationIcon } from '@blocksuite/icons/rc';
-import type { Meta, StoryFn } from '@storybook/react';
+import {
+ AfFiNeIcon,
+ ArrowRightBigIcon,
+ FolderIcon,
+} from '@blocksuite/icons/rc';
+import type { Meta } from '@storybook/react';
+import clsx from 'clsx';
+import { useCallback, useEffect, useState } from 'react';
+import { Switch } from '../switch';
import type { ButtonProps } from './button';
import { Button } from './button';
+import * as styles from './button.stories.css';
export default {
title: 'UI/Button',
component: Button,
- argTypes: {
- onClick: () => console.log('Click button'),
- },
} satisfies Meta
;
-const Template: StoryFn = args => ;
+// const Template: StoryFn = args => ;
-export const Default: StoryFn = Template.bind(undefined);
-Default.args = {
- type: 'default',
- children: 'This is a default button',
- icon: ,
+const types: ButtonProps['variant'][] = [
+ 'primary',
+ 'secondary',
+ 'plain',
+ 'error',
+ 'success',
+];
+const sizes: ButtonProps['size'][] = ['default', 'large', 'extraLarge'];
+
+const Groups = ({
+ children,
+ ...props
+}: Omit) => {
+ return (
+
+
+
+ | Type/Size |
+ {sizes.map(size => (
+ {size} |
+ ))}
+
+
+
+ {types.map(type => (
+
+ | {type} |
+ {sizes.map(size => (
+
+
+ {children ?? `${size} - ${type}`}
+
+ |
+ ))}
+
+ ))}
+
+
+ );
};
-export const Primary: StoryFn = Template.bind(undefined);
-Primary.args = {
- type: 'primary',
- children: 'Content',
- icon: ,
+export const Default = () => ;
+
+export const WithIcon = () => {
+ return } suffix={🚀} />;
};
-export const Disabled: StoryFn = Template.bind(undefined);
-Disabled.args = {
- disabled: true,
- children: 'This is a disabled button',
+export const Loading = () => {
+ const [loading, setLoading] = useState(false);
+
+ const toggleLoading = useCallback(() => setLoading(v => !v), []);
+
+ useEffect(() => {
+ setInterval(toggleLoading, 1000);
+ }, [toggleLoading]);
+
+ return } />;
};
-export const LargeSizeButton: StoryFn = Template.bind(undefined);
-LargeSizeButton.args = {
- size: 'large',
- children: 'This is a large button',
+export const OverrideViaClassName = () => {
+ const [overrideBg, setOverrideBg] = useState(false);
+ const [overrideTextColor, setOverrideTextColor] = useState(false);
+ const [overrideBorder, setOverrideBorder] = useState(false);
+ const [overrideFontSize, setOverrideFontSize] = useState(false);
+ const [overridePrefixSize, setOverridePrefixSize] = useState(false);
+ const [overrideSuffixSize, setOverrideSuffixSize] = useState(false);
+ const [overridePrefixColor, setOverridePrefixColor] = useState(false);
+ const [overrideSuffixColor, setOverrideSuffixColor] = useState(false);
+
+ return (
+
+
+
+ Override background color
+
+
+
+
+ Override text color
+
+
+
+
+ Override border color
+
+
+
+
+
+
+ Override prefix size
+
+
+
+
+ Override suffix size
+
+
+
+
+ Override prefix color
+
+
+
+
+ Override suffix color
+
+
+
+
+
}
+ suffix={
}
+ className={clsx({
+ [styles.overrideBackground]: overrideBg,
+ [styles.overrideTextColor]: overrideTextColor,
+ [styles.overrideBorder]: overrideBorder,
+ [styles.overrideFontSize]: overrideFontSize,
+ })}
+ prefixClassName={clsx({
+ [styles.overrideIconSize]: overridePrefixSize,
+ [styles.overrideIconColor]: overridePrefixColor,
+ })}
+ suffixClassName={clsx({
+ [styles.overrideIconSize]: overrideSuffixSize,
+ [styles.overrideIconColor]: overrideSuffixColor,
+ })}
+ />
+
+ );
};
-export const ExtraLargeSizeButton: StoryFn =
- Template.bind(undefined);
-ExtraLargeSizeButton.args = {
- size: 'extraLarge',
- children: 'This is a extra large button',
+export const FixedWidth = () => {
+ const widths = [60, 100, 120, 160, 180];
+ return (
+
+ {widths.map(width => (
+ } key={width} style={{ width }}>
+ This is a width fixed button
+
+ ))}
+
+ );
+};
+
+export const Disabled = () => {
+ return ;
};
diff --git a/packages/frontend/component/src/ui/button/button.tsx b/packages/frontend/component/src/ui/button/button.tsx
index 4a8ee87841..988405c22e 100644
--- a/packages/frontend/component/src/ui/button/button.tsx
+++ b/packages/frontend/component/src/ui/button/button.tsx
@@ -1,172 +1,168 @@
import clsx from 'clsx';
import type {
- FC,
+ CSSProperties,
HTMLAttributes,
- PropsWithChildren,
+ MouseEvent,
ReactElement,
} from 'react';
-import { forwardRef, useMemo } from 'react';
+import { cloneElement, forwardRef, useCallback } from 'react';
import { Loading } from '../loading';
-import { button, buttonIcon } from './button.css';
+import { Tooltip, type TooltipProps } from '../tooltip';
+import * as styles from './button.css';
export type ButtonType =
- | 'default'
| 'primary'
+ | 'secondary'
| 'plain'
| 'error'
- | 'warning'
| 'success'
- | 'processing';
-export type ButtonSize = 'default' | 'large' | 'extraLarge';
-type BaseButtonProps = {
- type?: ButtonType;
+ | 'custom';
+export type ButtonSize = 'default' | 'large' | 'extraLarge' | 'custom';
+
+export interface ButtonProps
+ extends Omit, 'type' | 'prefix'> {
+ /**
+ * Preset color scheme
+ * @default 'secondary'
+ */
+ variant?: ButtonType;
disabled?: boolean;
- icon?: ReactElement;
- iconPosition?: 'start' | 'end';
- shape?: 'default' | 'round' | 'circle';
+ /**
+ * By default, the button is `inline-flex`, set to `true` to make it `flex`
+ * @default false
+ */
block?: boolean;
+ /**
+ * Preset size, will be overridden by `style` or `className`
+ * @default 'default'
+ */
size?: ButtonSize;
+ /**
+ * Will show a loading spinner at `prefix` position
+ */
loading?: boolean;
- withoutHoverStyle?: boolean;
-};
-export type ButtonProps = PropsWithChildren &
- Omit, 'type'> & {
- componentProps?: {
- startIcon?: Omit;
- endIcon?: Omit;
- };
- };
+ /**
+ * By default, it is considered as an icon with preset size and color,
+ * can be overridden by `prefixClassName` and `prefixStyle`.
+ *
+ * If `loading` is true, will be replaced by a spinner.(`prefixClassName` and `prefixStyle` still work)
+ * */
+ prefix?: ReactElement;
+ prefixClassName?: string;
+ prefixStyle?: CSSProperties;
+ contentClassName?: string;
+ contentStyle?: CSSProperties;
-type IconButtonProps = PropsWithChildren &
- Omit, 'type'>;
+ /**
+ * By default, it is considered as an icon with preset size and color,
+ * can be overridden by `suffixClassName` and `suffixStyle`.
+ * */
+ suffix?: ReactElement;
+ suffixClassName?: string;
+ suffixStyle?: CSSProperties;
-const defaultProps = {
- type: 'default',
- disabled: false,
- shape: 'default',
- size: 'default',
- iconPosition: 'start',
- loading: false,
- withoutHoverStyle: false,
-} as const;
+ tooltip?: TooltipProps['content'];
+ tooltipOptions?: Partial>;
+}
-const ButtonIcon: FC = props => {
- const {
- size,
- icon,
- iconPosition = 'start',
- children,
- type,
- loading,
- withoutHoverStyle,
- ...otherProps
- } = {
- ...defaultProps,
- ...props,
- };
- const onlyIcon = icon && !children;
- return (
-
- {icon}
+const IconSlot = ({
+ icon,
+ loading,
+ className,
+ ...attrs
+}: {
+ icon?: ReactElement;
+ loading?: boolean;
+} & HTMLAttributes
) => {
+ const showLoadingHere = loading !== undefined;
+ const visible = icon || loading;
+ return visible ? (
+
+ {showLoadingHere && loading ? : null}
+ {icon && !loading
+ ? cloneElement(icon, {
+ width: '100%',
+ height: '100%',
+ ...icon.props,
+ })
+ : null}
- );
+ ) : null;
};
+
export const Button = forwardRef(
- (props, ref) => {
- const {
+ (
+ {
+ variant = 'secondary',
+ size = 'default',
children,
- type,
disabled,
- shape,
- size,
- icon: propsIcon,
- iconPosition,
block,
loading,
- withoutHoverStyle,
className,
+
+ prefix,
+ prefixClassName,
+ prefixStyle,
+ suffix,
+ suffixClassName,
+ suffixStyle,
+ contentClassName,
+ contentStyle,
+
+ tooltip,
+ tooltipOptions,
+ onClick,
+
...otherProps
- } = {
- ...defaultProps,
- ...props,
- } satisfies ButtonProps;
-
- const icon = useMemo(() => {
- if (loading) {
- return ;
- }
- return propsIcon;
- }, [propsIcon, loading]);
-
- const baseIconButtonProps = useMemo(() => {
- return {
- size,
- iconPosition,
- icon,
- type,
- disabled,
- loading,
- } as const;
- }, [disabled, icon, iconPosition, loading, size, type]);
+ },
+ ref
+ ) => {
+ const handleClick = useCallback(
+ (e: MouseEvent) => {
+ if (loading || disabled) return;
+ onClick?.(e);
+ },
+ [disabled, loading, onClick]
+ );
return (
-
- {icon && iconPosition === 'start' ? (
-
+
+
- ) : null}
- {children}
- {icon && iconPosition === 'end' ? (
-
+ {children}
+
+ ) : null}
+
- ) : null}
-
+
+
);
}
);
diff --git a/packages/frontend/component/src/ui/button/icon-button.stories.tsx b/packages/frontend/component/src/ui/button/icon-button.stories.tsx
index 6509ea4052..943f1ef408 100644
--- a/packages/frontend/component/src/ui/button/icon-button.stories.tsx
+++ b/packages/frontend/component/src/ui/button/icon-button.stories.tsx
@@ -1,49 +1,153 @@
-import { InformationIcon } from '@blocksuite/icons/rc';
-import type { Meta, StoryFn } from '@storybook/react';
+import { AfFiNeIcon } from '@blocksuite/icons/rc';
+import type { Meta } from '@storybook/react';
+import clsx from 'clsx';
+import { type ReactElement, useCallback, useEffect, useState } from 'react';
+import { Switch } from '../switch';
+import * as styles from './button.stories.css';
import type { IconButtonProps } from './icon-button';
import { IconButton } from './icon-button';
+
export default {
title: 'UI/IconButton',
component: IconButton,
- argTypes: {
- onClick: () => console.log('Click button'),
- },
} satisfies Meta;
-const Template: StoryFn = args => ;
+const types: IconButtonProps['variant'][] = ['plain', 'solid', 'danger'];
+const sizes: IconButtonProps['size'][] = ['12', '14', '16', '20', '24'];
-export const Plain: StoryFn = Template.bind(undefined);
-Plain.args = {
- children: ,
+const Groups = ({
+ children,
+ ...props
+}: Omit & {
+ children?: ReactElement;
+}) => {
+ return (
+
+
+
+ | Type/Size |
+ {sizes.map(size => (
+ {size} |
+ ))}
+
+
+
+ {types.map(type => (
+
+ | {type} |
+ {sizes.map(size => (
+
+
+ {children ?? }
+
+ |
+ ))}
+
+ ))}
+
+
+ );
};
-export const Primary: StoryFn = Template.bind(undefined);
-Primary.args = {
- type: 'primary',
- icon: ,
+export const Default = () => ;
+
+export const Loading = () => {
+ const [loading, setLoading] = useState(false);
+
+ const toggleLoading = useCallback(() => setLoading(v => !v), []);
+
+ useEffect(() => {
+ setInterval(toggleLoading, 1000);
+ }, [toggleLoading]);
+
+ return ;
};
-export const Disabled: StoryFn = Template.bind(undefined);
-Disabled.args = {
- disabled: true,
- icon: ,
+export const OverrideViaClassName = () => {
+ const [overrideBg, setOverrideBg] = useState(false);
+ const [overrideBorder, setOverrideBorder] = useState(false);
+ const [overridePrefixColor, setOverridePrefixColor] = useState(false);
+
+ return (
+
+
+
+ Override background color
+
+
+
+
+ Override border color
+
+
+
+
+ Override icon color
+
+
+
+
+
+
+ );
};
-export const ExtraSmallSizeButton: StoryFn =
- Template.bind(undefined);
-ExtraSmallSizeButton.args = {
- size: 'extraSmall',
- icon: ,
-};
-export const SmallSizeButton: StoryFn =
- Template.bind(undefined);
-SmallSizeButton.args = {
- size: 'small',
- icon: ,
-};
-export const LargeSizeButton: StoryFn =
- Template.bind(undefined);
-LargeSizeButton.args = {
- size: 'large',
- icon: ,
+
+export const CustomSize = () => {
+ const sizes = [
+ [13, 2],
+ [15, 2],
+ [17, 2],
+ [19, 2],
+ [21, 3],
+ [23, 3],
+ [25, 3],
+ [27, 3],
+ [29, 4],
+ [31, 4],
+ [33, 4],
+ [35, 4],
+ ];
+ return types.map(type => {
+ return (
+
+
+ {sizes.map(size => (
+
+
+
+
+
+
Size: {size[0]}px
+
Padding: {size[1]}px
+
+ ))}
+
+
+ );
+ });
};
+
+export const Disabled = () => ;
diff --git a/packages/frontend/component/src/ui/button/icon-button.tsx b/packages/frontend/component/src/ui/button/icon-button.tsx
index 5447905ae2..7524de5eb7 100644
--- a/packages/frontend/component/src/ui/button/icon-button.tsx
+++ b/packages/frontend/component/src/ui/button/icon-button.tsx
@@ -1,85 +1,78 @@
+import { assignInlineVars } from '@vanilla-extract/dynamic';
import clsx from 'clsx';
-import type { HTMLAttributes, PropsWithChildren, ReactElement } from 'react';
-import { forwardRef } from 'react';
+import { type CSSProperties, forwardRef, type ReactElement } from 'react';
-import { Loading } from '../loading';
-import type { ButtonType } from './button';
-import { iconButton } from './button.css';
+import { Button, type ButtonProps } from './button';
+import { iconButton, iconSizeVar } from './button.css';
-export type IconButtonSize = 'default' | 'large' | 'small' | 'extraSmall';
-export type IconButtonProps = Omit, 'type'> &
- PropsWithChildren<{
- type?: ButtonType;
- disabled?: boolean;
- size?: IconButtonSize;
- loading?: boolean;
- withoutPadding?: boolean;
- active?: boolean;
- withoutHoverStyle?: boolean;
- icon?: ReactElement;
- }>;
-
-const defaultProps = {
- type: 'plain',
- disabled: false,
- size: 'default',
- loading: false,
- withoutPadding: false,
- active: false,
- withoutHoverStyle: false,
-} as const;
+export interface IconButtonProps
+ extends Omit<
+ ButtonProps,
+ | 'variant'
+ | 'size'
+ | 'prefix'
+ | 'suffix'
+ | 'children'
+ | 'prefixClassName'
+ | 'prefixStyle'
+ | 'suffix'
+ | 'suffixClassName'
+ | 'suffixStyle'
+ > {
+ /** Icon element */
+ children?: ReactElement;
+ /** Same as `children`, compatibility of the old API */
+ icon?: ReactElement;
+ variant?: 'plain' | 'solid' | 'danger' | 'custom';
+ /**
+ * Use preset size,
+ * or use custom size(px) (default padding is `2px`, have to override yourself)
+ *
+ * > These presets size are referenced from the design system.
+ * > The number is the size of the icon, the button size is calculated based on the icon size + padding.
+ * > OR, you can define `width` and `height` in `style` or `className` directly.
+ */
+ size?: '12' | '14' | '16' | '20' | '24' | number;
+ iconClassName?: string;
+ iconStyle?: CSSProperties;
+}
export const IconButton = forwardRef(
- (props, ref) => {
- const {
- type,
- size,
- withoutPadding,
- children,
- disabled,
- loading,
- active,
- withoutHoverStyle,
- icon: propsIcon,
+ (
+ {
+ variant = 'plain',
+ size = '20',
+ style,
className,
+ children,
+ icon,
+ iconClassName,
+ iconStyle,
...otherProps
- } = {
- ...defaultProps,
- ...props,
- };
+ },
+ ref
+ ) => {
+ const validatedSize = isNaN(parseInt(size as string, 10)) ? 16 : size;
return (
-
- {loading ? : children || propsIcon}
-
+ />
);
}
);
diff --git a/packages/frontend/component/src/ui/button/interface.ts b/packages/frontend/component/src/ui/button/interface.ts
deleted file mode 100644
index 380f7872c1..0000000000
--- a/packages/frontend/component/src/ui/button/interface.ts
+++ /dev/null
@@ -1,26 +0,0 @@
-import type {
- CSSProperties,
- HTMLAttributes,
- PropsWithChildren,
- ReactElement,
-} from 'react';
-
-export const SIZE_SMALL = 'small' as const;
-export const SIZE_MIDDLE = 'middle' as const;
-export const SIZE_DEFAULT = 'default' as const;
-
-export type ButtonProps = PropsWithChildren &
- Omit, 'type'> & {
- size?: typeof SIZE_SMALL | typeof SIZE_MIDDLE | typeof SIZE_DEFAULT;
- disabled?: boolean;
- hoverBackground?: CSSProperties['background'];
- hoverColor?: CSSProperties['color'];
- hoverStyle?: CSSProperties;
- icon?: ReactElement;
- iconPosition?: 'start' | 'end';
- shape?: 'default' | 'round' | 'circle';
- type?: 'primary' | 'light' | 'warning' | 'danger' | 'default';
- bold?: boolean;
- loading?: boolean;
- noBorder?: boolean;
- };
diff --git a/packages/frontend/component/src/ui/button/utils.ts b/packages/frontend/component/src/ui/button/utils.ts
deleted file mode 100644
index 09a3a6a0c7..0000000000
--- a/packages/frontend/component/src/ui/button/utils.ts
+++ /dev/null
@@ -1,89 +0,0 @@
-import type { ButtonProps } from './interface';
-
-export const getButtonColors = (
- type: ButtonProps['type'],
- disabled: boolean,
- extend?: {
- hoverBackground: ButtonProps['hoverBackground'];
- hoverColor: ButtonProps['hoverColor'];
- hoverStyle: ButtonProps['hoverStyle'];
- }
-) => {
- switch (type) {
- case 'primary':
- return {
- background: 'var(--affine-primary-color)',
- color: 'var(--affine-white)',
- borderColor: 'var(--affine-primary-color)',
- backgroundBlendMode: 'overlay',
- opacity: disabled ? '.4' : '1',
- '.affine-button-icon': {
- color: 'var(--affine-white)',
- },
- ':hover': {
- background:
- 'linear-gradient(var(--affine-primary-color),var(--affine-primary-color)),var(--affine-hover-color)',
- },
- };
- case 'light':
- return {
- background: 'var(--affine-tertiary-color)',
- color: disabled
- ? 'var(--affine-text-disable-color)'
- : 'var(--affine-text-emphasis-color)',
- borderColor: 'var(--affine-tertiary-color)',
- '.affine-button-icon': {
- borderColor: 'var(--affine-text-emphasis-color)',
- },
- ':hover': {
- borderColor: disabled
- ? 'var(--affine-disable-color)'
- : 'var(--affine-text-emphasis-color)',
- },
- };
- case 'warning':
- return {
- background: 'var(--affine-background-warning-color)',
- color: 'var(--affine-warning-color)',
- borderColor: 'var(--affine-background-warning-color)',
- '.affine-button-icon': {
- color: 'var(--affine-warning-color)',
- },
- ':hover': {
- borderColor: 'var(--affine-warning-color)',
- color: extend?.hoverColor,
- background: extend?.hoverBackground,
- ...extend?.hoverStyle,
- },
- };
- case 'danger':
- return {
- background: 'var(--affine-background-error-color)',
- color: 'var(--affine-error-color)',
- borderColor: 'var(--affine-background-error-color)',
- '.affine-button-icon': {
- color: 'var(--affine-error-color)',
- },
- ':hover': {
- borderColor: 'var(--affine-error-color)',
- color: extend?.hoverColor,
- background: extend?.hoverBackground,
- ...extend?.hoverStyle,
- },
- };
- default:
- return {
- color: 'var(--affine-text-primary-color)',
- borderColor: 'var(--affine-border-color)',
- ':hover': {
- borderColor: 'var(--affine-primary-color)',
- color: extend?.hoverColor ?? 'var(--affine-primary-color)',
- '.affine-button-icon': {
- color: extend?.hoverColor ?? 'var(--affine-primary-color)',
- background: extend?.hoverBackground,
- ...extend?.hoverStyle,
- },
- },
- };
- }
-};
diff --git a/packages/frontend/component/src/ui/date-picker/calendar/items.tsx b/packages/frontend/component/src/ui/date-picker/calendar/items.tsx
index ec35aa4dc0..13182f589f 100644
--- a/packages/frontend/component/src/ui/date-picker/calendar/items.tsx
+++ b/packages/frontend/component/src/ui/date-picker/calendar/items.tsx
@@ -134,8 +134,7 @@ export const NavButtons = memo(function NavButtons({
{
+ // allow `string` such as `16px` | `100%` | `1em`
+ const sizeWithUnit = size ? withUnit(size, 'px') : '16px';
return (
{properties.length === 0 || manager.readonly ? null : (
- }
- />
+
+
+
)}
@@ -715,15 +713,12 @@ export const PagePropertiesTableHeader = ({
className={styles.tableHeaderCollapseButtonWrapper}
data-testid="page-info-collapse"
>
-
- }
- />
+
+
+
@@ -1056,8 +1051,8 @@ export const PagePropertiesAddProperty = () => {
return (
) : (
-
+ } onClick={clickFilter} />
)}
{showFilter ? (
diff --git a/packages/frontend/core/src/components/page-list/view/save-as-collection-button.tsx b/packages/frontend/core/src/components/page-list/view/save-as-collection-button.tsx
index 9525df05e0..1c5fb163e9 100644
--- a/packages/frontend/core/src/components/page-list/view/save-as-collection-button.tsx
+++ b/packages/frontend/core/src/components/page-list/view/save-as-collection-button.tsx
@@ -35,7 +35,7 @@ export const SaveAsCollectionButton = ({
}
+ prefix={}
className={styles.button}
>
{t['com.affine.editCollection.saveCollection']()}
diff --git a/packages/frontend/core/src/components/page-list/virtualized-trash-list.tsx b/packages/frontend/core/src/components/page-list/virtualized-trash-list.tsx
index 667eb0eba9..0713e9db5f 100644
--- a/packages/frontend/core/src/components/page-list/virtualized-trash-list.tsx
+++ b/packages/frontend/core/src/components/page-list/virtualized-trash-list.tsx
@@ -75,7 +75,7 @@ export const VirtualizedTrashList = () => {
cancelText: t['Cancel'](),
confirmText: t['com.affine.trashOperation.delete'](),
confirmButtonOptions: {
- type: 'error',
+ variant: 'error',
},
onConfirm: handleMultiDelete,
});
diff --git a/packages/frontend/core/src/components/pure/header-drop-down-button/index.tsx b/packages/frontend/core/src/components/pure/header-drop-down-button/index.tsx
index 10b1aef4eb..d77c081a08 100644
--- a/packages/frontend/core/src/components/pure/header-drop-down-button/index.tsx
+++ b/packages/frontend/core/src/components/pure/header-drop-down-button/index.tsx
@@ -15,8 +15,6 @@ export const HeaderDropDownButton = forwardRef<
{...props}
data-testid="header-dropDownButton"
className={headerMenuTrigger}
- withoutHoverStyle={true}
- type="plain"
>
diff --git a/packages/frontend/core/src/components/pure/header-drop-down-button/styles.css.ts b/packages/frontend/core/src/components/pure/header-drop-down-button/styles.css.ts
index 5feb36501e..a7563277e0 100644
--- a/packages/frontend/core/src/components/pure/header-drop-down-button/styles.css.ts
+++ b/packages/frontend/core/src/components/pure/header-drop-down-button/styles.css.ts
@@ -2,7 +2,7 @@ import { cssVar } from '@toeverything/theme';
import { style } from '@vanilla-extract/css';
export const headerMenuTrigger = style({
selectors: {
- '&[data-state=open], &:hover': {
+ '&[data-state=open]': {
backgroundColor: cssVar('hoverColor'),
},
},
diff --git a/packages/frontend/core/src/components/pure/trash-page-footer/index.tsx b/packages/frontend/core/src/components/pure/trash-page-footer/index.tsx
index 55707166ee..55d20f742f 100644
--- a/packages/frontend/core/src/components/pure/trash-page-footer/index.tsx
+++ b/packages/frontend/core/src/components/pure/trash-page-footer/index.tsx
@@ -1,6 +1,5 @@
import { Button } from '@affine/component/ui/button';
import { ConfirmModal } from '@affine/component/ui/modal';
-import { Tooltip } from '@affine/component/ui/tooltip';
import { useI18n } from '@affine/i18n';
import { DeleteIcon, ResetIcon } from '@blocksuite/icons/rc';
import { DocService, useService, WorkspaceService } from '@toeverything/infra';
@@ -50,30 +49,23 @@ export const TrashPageFooter = () => {
>
{hintText}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
}
+ prefixClassName={styles.icon}
+ />
+
}
+ prefixClassName={styles.icon}
+ />
{
description={t['com.affine.trashOperation.delete.description']()}
confirmText={t['com.affine.trashOperation.delete']()}
confirmButtonOptions={{
- type: 'error',
+ variant: 'error',
}}
open={open}
onConfirm={onConfirmDelete}
diff --git a/packages/frontend/core/src/components/pure/trash-page-footer/styles.css.ts b/packages/frontend/core/src/components/pure/trash-page-footer/styles.css.ts
index 1e7179c752..1937ba329d 100644
--- a/packages/frontend/core/src/components/pure/trash-page-footer/styles.css.ts
+++ b/packages/frontend/core/src/components/pure/trash-page-footer/styles.css.ts
@@ -28,12 +28,10 @@ export const deleteHintText = style({
overflow: 'hidden',
});
export const buttonContainer = style({
- color: cssVar('pureWhite'),
padding: '8px 18px',
- fontSize: '20px',
height: '36px',
});
export const icon = style({
- display: 'flex',
- alignContent: 'center',
+ width: 20,
+ height: 20,
});
diff --git a/packages/frontend/core/src/components/root-app-sidebar/index.css.ts b/packages/frontend/core/src/components/root-app-sidebar/index.css.ts
index 80ab3e32f4..88e267fe42 100644
--- a/packages/frontend/core/src/components/root-app-sidebar/index.css.ts
+++ b/packages/frontend/core/src/components/root-app-sidebar/index.css.ts
@@ -24,18 +24,6 @@ export const workspaceWrapper = style({
flex: 1,
});
-export const userInfoWrapper = style({
- flexShrink: 0,
- width: 'auto',
- height: 'auto',
- padding: '4px 0',
-});
-
-// TODO(@catsjuice):
-globalStyle(`button.${userInfoWrapper} > span`, {
- lineHeight: 0,
-});
-
export const operationMenu = style({
display: 'flex',
flexDirection: 'column',
diff --git a/packages/frontend/core/src/components/root-app-sidebar/trash-button.tsx b/packages/frontend/core/src/components/root-app-sidebar/trash-button.tsx
index 2d51c27ab5..9d5e70f1cd 100644
--- a/packages/frontend/core/src/components/root-app-sidebar/trash-button.tsx
+++ b/packages/frontend/core/src/components/root-app-sidebar/trash-button.tsx
@@ -44,7 +44,7 @@ export const TrashButton = () => {
),
confirmText: t.Delete(),
confirmButtonOptions: {
- type: 'error',
+ variant: 'error',
},
onConfirm() {
docRecord.moveToTrash();
diff --git a/packages/frontend/core/src/components/root-app-sidebar/user-info.tsx b/packages/frontend/core/src/components/root-app-sidebar/user-info.tsx
index 6b3e95b71c..fdf20e6d1c 100644
--- a/packages/frontend/core/src/components/root-app-sidebar/user-info.tsx
+++ b/packages/frontend/core/src/components/root-app-sidebar/user-info.tsx
@@ -1,8 +1,8 @@
import {
Avatar,
- Button,
Divider,
ErrorMessage,
+ IconButton,
Menu,
MenuIcon,
MenuItem,
@@ -52,13 +52,9 @@ const menuContentOptions: MenuProps['contentOptions'] = {
const AuthorizedUserInfo = ({ account }: { account: AuthAccountInfo }) => {
return (
} contentOptions={menuContentOptions}>
-
+
-
+
);
};
@@ -71,14 +67,14 @@ const UnauthorizedUserInfo = () => {
}, [setOpen]);
return (
-
-
-
+
+
);
};
diff --git a/packages/frontend/core/src/components/workspace-upgrade/upgrade.css.ts b/packages/frontend/core/src/components/workspace-upgrade/upgrade.css.ts
index 5deb8329c2..d5f7a31381 100644
--- a/packages/frontend/core/src/components/workspace-upgrade/upgrade.css.ts
+++ b/packages/frontend/core/src/components/workspace-upgrade/upgrade.css.ts
@@ -1,5 +1,5 @@
import { cssVar } from '@toeverything/theme';
-import { keyframes, style } from '@vanilla-extract/css';
+import { style } from '@vanilla-extract/css';
export const layout = style({
margin: '80px auto',
maxWidth: '536px',
@@ -19,17 +19,3 @@ export const upgradeTips = style({
lineHeight: '20px',
textAlign: 'center',
});
-const rotate = keyframes({
- '0%': {
- transform: 'rotate(0deg)',
- },
- '50%': {
- transform: 'rotate(180deg)',
- },
- '100%': {
- transform: 'rotate(360deg)',
- },
-});
-export const loadingIcon = style({
- animation: `${rotate} 1.2s infinite linear`,
-});
diff --git a/packages/frontend/core/src/components/workspace-upgrade/upgrade.tsx b/packages/frontend/core/src/components/workspace-upgrade/upgrade.tsx
index c77075d244..24c36ced7a 100644
--- a/packages/frontend/core/src/components/workspace-upgrade/upgrade.tsx
+++ b/packages/frontend/core/src/components/workspace-upgrade/upgrade.tsx
@@ -54,16 +54,9 @@ export const WorkspaceUpgrade = function WorkspaceUpgrade() {
data-testid="upgrade-workspace-button"
onClick={onButtonClick}
size="extraLarge"
- icon={
- error ? (
-
- ) : (
-
- )
- }
- type={error ? 'error' : 'default'}
+ loading={upgrading}
+ prefix={error ? : }
+ variant={error ? 'error' : 'secondary'}
>
{error
? t['com.affine.upgrade.button-text.error']()
diff --git a/packages/frontend/core/src/hooks/affine/use-enable-cloud.tsx b/packages/frontend/core/src/hooks/affine/use-enable-cloud.tsx
index e01f160383..70ea74ca4a 100644
--- a/packages/frontend/core/src/hooks/affine/use-enable-cloud.tsx
+++ b/packages/frontend/core/src/hooks/affine/use-enable-cloud.tsx
@@ -89,7 +89,7 @@ export const useEnableCloud = () => {
? t['Enable']()
: t['Sign in and Enable'](),
confirmButtonOptions: {
- type: 'primary',
+ variant: 'primary',
['data-testid' as string]: 'confirm-enable-affine-cloud-button',
},
onConfirm: async () =>
diff --git a/packages/frontend/core/src/modules/app-tabs-header/views/app-tabs-header.tsx b/packages/frontend/core/src/modules/app-tabs-header/views/app-tabs-header.tsx
index f2477b2f1c..cdbd0f1194 100644
--- a/packages/frontend/core/src/modules/app-tabs-header/views/app-tabs-header.tsx
+++ b/packages/frontend/core/src/modules/app-tabs-header/views/app-tabs-header.tsx
@@ -362,11 +362,15 @@ export const AppTabsHeader = ({
ref={spacerDropTargetRef}
data-dragged-over={draggedOver}
>
-
-
-
+ }
+ />
-
+
{environment.isDesktop && environment.isWindows ? (
diff --git a/packages/frontend/core/src/modules/explorer/views/nodes/collection/operations.tsx b/packages/frontend/core/src/modules/explorer/views/nodes/collection/operations.tsx
index b422eeb55d..9fba8ee0e1 100644
--- a/packages/frontend/core/src/modules/explorer/views/nodes/collection/operations.tsx
+++ b/packages/frontend/core/src/modules/explorer/views/nodes/collection/operations.tsx
@@ -95,7 +95,7 @@ export const useExplorerCollectionNodeOperations = (
cancelText: t['Cancel'](),
confirmText: t['Confirm'](),
confirmButtonOptions: {
- type: 'primary',
+ variant: 'primary',
},
onConfirm: createAndAddDocument,
});
@@ -129,11 +129,7 @@ export const useExplorerCollectionNodeOperations = (
index: 0,
inline: true,
view: (
-
+
),
diff --git a/packages/frontend/core/src/modules/explorer/views/nodes/doc/operations.tsx b/packages/frontend/core/src/modules/explorer/views/nodes/doc/operations.tsx
index fc749611e4..ab51c555f6 100644
--- a/packages/frontend/core/src/modules/explorer/views/nodes/doc/operations.tsx
+++ b/packages/frontend/core/src/modules/explorer/views/nodes/doc/operations.tsx
@@ -61,7 +61,7 @@ export const useExplorerDocNodeOperations = (
confirmText: t['com.affine.moveToTrash.confirmModal.confirm'](),
cancelText: t['com.affine.moveToTrash.confirmModal.cancel'](),
confirmButtonOptions: {
- type: 'error',
+ variant: 'error',
},
onConfirm() {
docRecord.moveToTrash();
diff --git a/packages/frontend/core/src/modules/explorer/views/nodes/folder/index.tsx b/packages/frontend/core/src/modules/explorer/views/nodes/folder/index.tsx
index 6c0c783f79..4d50b09ddf 100644
--- a/packages/frontend/core/src/modules/explorer/views/nodes/folder/index.tsx
+++ b/packages/frontend/core/src/modules/explorer/views/nodes/folder/index.tsx
@@ -633,7 +633,7 @@ export const ExplorerFolderNodeFolder = ({
index: 0,
inline: true,
view: (
-
+
),
diff --git a/packages/frontend/core/src/modules/explorer/views/nodes/tag/operations.tsx b/packages/frontend/core/src/modules/explorer/views/nodes/tag/operations.tsx
index ed93507df6..8f36c8b484 100644
--- a/packages/frontend/core/src/modules/explorer/views/nodes/tag/operations.tsx
+++ b/packages/frontend/core/src/modules/explorer/views/nodes/tag/operations.tsx
@@ -103,7 +103,7 @@ export const useExplorerTagNodeOperations = (
index: 0,
inline: true,
view: (
-
+
),
diff --git a/packages/frontend/core/src/modules/explorer/views/sections/collections/index.tsx b/packages/frontend/core/src/modules/explorer/views/sections/collections/index.tsx
index 4cf71a38e8..6ddde6c8bb 100644
--- a/packages/frontend/core/src/modules/explorer/views/sections/collections/index.tsx
+++ b/packages/frontend/core/src/modules/explorer/views/sections/collections/index.tsx
@@ -63,7 +63,7 @@ export const ExplorerCollections = () => {
diff --git a/packages/frontend/core/src/modules/explorer/views/sections/favorites/index.tsx b/packages/frontend/core/src/modules/explorer/views/sections/favorites/index.tsx
index 27c0404f9b..26618293fc 100644
--- a/packages/frontend/core/src/modules/explorer/views/sections/favorites/index.tsx
+++ b/packages/frontend/core/src/modules/explorer/views/sections/favorites/index.tsx
@@ -239,7 +239,7 @@ export const ExplorerFavorites = () => {
diff --git a/packages/frontend/core/src/modules/explorer/views/sections/migration-favorites/index.tsx b/packages/frontend/core/src/modules/explorer/views/sections/migration-favorites/index.tsx
index 4033bd6c31..8cee8a9eb6 100644
--- a/packages/frontend/core/src/modules/explorer/views/sections/migration-favorites/index.tsx
+++ b/packages/frontend/core/src/modules/explorer/views/sections/migration-favorites/index.tsx
@@ -52,7 +52,7 @@ export const ExplorerMigrationFavorites = () => {
confirmText:
t['com.affine.rootAppSidebar.migration-data.clean-all.confirm'](),
confirmButtonOptions: {
- type: 'primary',
+ variant: 'primary',
},
cancelText:
t['com.affine.rootAppSidebar.migration-data.clean-all.cancel'](),
@@ -79,13 +79,12 @@ export const ExplorerMigrationFavorites = () => {
t['com.affine.rootAppSidebar.migration-data.help.description'](),
confirmText: t['com.affine.rootAppSidebar.migration-data.help.confirm'](),
confirmButtonOptions: {
- type: 'primary',
+ variant: 'primary',
},
cancelText:
t['com.affine.rootAppSidebar.migration-data.help.clean-all'](),
cancelButtonOptions: {
- icon: ,
- type: 'default',
+ prefix: ,
onClick: () => {
requestAnimationFrame(() => {
handleClickClear();
@@ -114,13 +113,13 @@ export const ExplorerMigrationFavorites = () => {
diff --git a/packages/frontend/core/src/modules/explorer/views/sections/old-favorites/index.tsx b/packages/frontend/core/src/modules/explorer/views/sections/old-favorites/index.tsx
index fe92122246..5b3796b3dc 100644
--- a/packages/frontend/core/src/modules/explorer/views/sections/old-favorites/index.tsx
+++ b/packages/frontend/core/src/modules/explorer/views/sections/old-favorites/index.tsx
@@ -184,7 +184,7 @@ export const ExplorerOldFavorites = () => {
diff --git a/packages/frontend/core/src/modules/explorer/views/sections/organize/index.tsx b/packages/frontend/core/src/modules/explorer/views/sections/organize/index.tsx
index 5faf2d9031..aa5fa614c7 100644
--- a/packages/frontend/core/src/modules/explorer/views/sections/organize/index.tsx
+++ b/packages/frontend/core/src/modules/explorer/views/sections/organize/index.tsx
@@ -122,7 +122,7 @@ export const ExplorerOrganize = () => {
diff --git a/packages/frontend/core/src/modules/explorer/views/sections/tags/index.tsx b/packages/frontend/core/src/modules/explorer/views/sections/tags/index.tsx
index b6ae827c8d..afa972ede0 100644
--- a/packages/frontend/core/src/modules/explorer/views/sections/tags/index.tsx
+++ b/packages/frontend/core/src/modules/explorer/views/sections/tags/index.tsx
@@ -53,7 +53,7 @@ export const ExplorerTags = () => {
diff --git a/packages/frontend/core/src/modules/explorer/views/tree/node.tsx b/packages/frontend/core/src/modules/explorer/views/tree/node.tsx
index 1995eb7b48..7102be1456 100644
--- a/packages/frontend/core/src/modules/explorer/views/tree/node.tsx
+++ b/packages/frontend/core/src/modules/explorer/views/tree/node.tsx
@@ -344,8 +344,7 @@ export const ExplorerTreeNode = ({
))}
>
diff --git a/packages/frontend/core/src/modules/find-in-page/view/find-in-page-modal.css.ts b/packages/frontend/core/src/modules/find-in-page/view/find-in-page-modal.css.ts
index 842a11f5a7..24f7466c5e 100644
--- a/packages/frontend/core/src/modules/find-in-page/view/find-in-page-modal.css.ts
+++ b/packages/frontend/core/src/modules/find-in-page/view/find-in-page-modal.css.ts
@@ -1,4 +1,5 @@
import { cssVar } from '@toeverything/theme';
+import { cssVarV2 } from '@toeverything/theme/v2';
import { createVar, keyframes, style } from '@vanilla-extract/css';
export const animationTimeout = createVar();
@@ -52,6 +53,7 @@ export const modalContent = style({
// :focus-visible will set outline
outline: 'none',
display: 'flex',
+ gap: 8,
alignItems: 'center',
justifyContent: 'space-between',
border: `0.5px solid ${cssVar('borderColor')}`,
@@ -133,22 +135,13 @@ export const count = style({
});
export const arrowButton = style({
- padding: '4px',
- fontSize: '24px',
- width: '32px',
- height: '32px',
+ width: 32,
+ height: 32,
flexShrink: 0,
border: '1px solid',
- borderColor: cssVar('borderColor'),
- color: cssVar('iconSecondary'),
- alignItems: 'baseline',
- background: 'transparent',
+ borderColor: cssVarV2('layer/border'),
selectors: {
- '&:hover': {
- color: cssVar('iconColor'),
- },
'&.backward': {
- marginLeft: '8px',
borderRadius: '4px 0 0 4px',
},
'&.forward': {
@@ -157,12 +150,3 @@ export const arrowButton = style({
},
},
});
-export const closeButton = style({
- padding: '4px',
- fontSize: '20px',
- width: '24px',
- height: '24px',
- flexShrink: 0,
- color: cssVar('iconColor'),
- marginLeft: '8px',
-});
diff --git a/packages/frontend/core/src/modules/find-in-page/view/find-in-page-modal.tsx b/packages/frontend/core/src/modules/find-in-page/view/find-in-page-modal.tsx
index 96aa2663b9..4f97a2dcf4 100644
--- a/packages/frontend/core/src/modules/find-in-page/view/find-in-page-modal.tsx
+++ b/packages/frontend/core/src/modules/find-in-page/view/find-in-page-modal.tsx
@@ -1,4 +1,4 @@
-import { Button, IconButton, observeResize } from '@affine/component';
+import { IconButton, observeResize } from '@affine/component';
import {
ArrowDownSmallIcon,
ArrowUpSmallIcon,
@@ -182,63 +182,58 @@ export const FindInPageModal = () => {
className={styles.modalContent}
data-state={status}
>
-
-
-
-
- handleValueChange(e.target.value)}
- />
-
-
-
- {value.length > 0 && result && result.matches !== 0 ? (
- <>
- {result?.activeMatchOrdinal || 0}
- /
- {result?.matches || 0}
- >
- ) : value.length ? (
- No matches
- ) : null}
-
+
+
+
+ handleValueChange(e.target.value)}
+ />
+
+
+ {value.length > 0 && result && result.matches !== 0 ? (
+ <>
+ {result?.activeMatchOrdinal || 0}
+ /
+ {result?.matches || 0}
+ >
+ ) : value.length ? (
+ No matches
+ ) : null}
+
+
-
+
-
-
-
}
+ />
+
-
-
+ icon={}
+ />
-
-
-
+
+
} />
diff --git a/packages/frontend/core/src/modules/navigation/view/navigation-buttons.tsx b/packages/frontend/core/src/modules/navigation/view/navigation-buttons.tsx
index 1c79c2c962..fe7f8f598a 100644
--- a/packages/frontend/core/src/modules/navigation/view/navigation-buttons.tsx
+++ b/packages/frontend/core/src/modules/navigation/view/navigation-buttons.tsx
@@ -1,4 +1,4 @@
-import { IconButton, Tooltip } from '@affine/component';
+import { IconButton } from '@affine/component';
import { useI18n } from '@affine/i18n';
import { ArrowLeftSmallIcon, ArrowRightSmallIcon } from '@blocksuite/icons/rc';
import { useLiveData, useService } from '@toeverything/infra';
@@ -8,6 +8,8 @@ import { useGeneralShortcuts } from '../../../hooks/affine/use-shortcuts';
import { NavigatorService } from '../services/navigator';
import * as styles from './navigation-buttons.css';
+const tooltipSideBottom = { side: 'bottom' as const };
+
export const NavigationButtons = () => {
const t = useI18n();
@@ -63,34 +65,29 @@ export const NavigationButtons = () => {
return null;
}
+ // TODO(@CatsJuice): tooltip with shortcut
return (
-
-
-
-
-
-
+
+
-
-
-
-
+
+
);
};
diff --git a/packages/frontend/core/src/modules/peek-view/view/image-preview/index.tsx b/packages/frontend/core/src/modules/peek-view/view/image-preview/index.tsx
index 5b87cea48b..72e99a0889 100644
--- a/packages/frontend/core/src/modules/peek-view/view/image-preview/index.tsx
+++ b/packages/frontend/core/src/modules/peek-view/view/image-preview/index.tsx
@@ -1,6 +1,5 @@
import { toast } from '@affine/component';
import { Button, IconButton } from '@affine/component/ui/button';
-import { Tooltip } from '@affine/component/ui/tooltip';
import { useAsyncCallback } from '@affine/core/hooks/affine-async-hooks';
import type { ImageBlockModel } from '@blocksuite/blocks';
import { assertExists } from '@blocksuite/global/utils';
@@ -100,30 +99,6 @@ export type ImagePreviewModalProps = {
blockId: string;
};
-const ButtonWithTooltip = ({
- icon,
- tooltip,
- disabled,
- ...props
-}: PropsWithChildren<{
- disabled?: boolean;
- icon?: ReactElement;
- tooltip: string;
- onClick: () => void;
- className?: string;
-}>) => {
- const element = icon ? (
-
- ) : (
-
- );
- if (disabled) {
- return element;
- } else {
- return {element};
- }
-};
-
const ImagePreviewModalImpl = ({
docId,
blockId,
@@ -362,7 +337,7 @@ const ImagePreviewModalImpl = ({
) : null}
-
}
@@ -372,7 +347,7 @@ const ImagePreviewModalImpl = ({
{`${blocks.length ? cursor + 1 : 0}/${blocks.length}`}
-
}
@@ -380,40 +355,41 @@ const ImagePreviewModalImpl = ({
onClick={() => goto(cursor + 1)}
/>
-
}
onClick={() => resetZoom()}
/>
-
}
onClick={zoomOut}
/>
-
{`${(currentScale * 100).toFixed(0)}%`}
-
+
-
}
onClick={zoomIn}
/>
-
}
onClick={downloadHandler}
/>
-
}
@@ -422,13 +398,13 @@ const ImagePreviewModalImpl = ({
{blockModel && !blockModel.doc.readonly && (
<>
-
}
disabled={blocks.length === 0}
onClick={() => deleteHandler(cursor)}
+ variant="danger"
/>
>
)}
diff --git a/packages/frontend/core/src/modules/peek-view/view/peek-view-controls.css.ts b/packages/frontend/core/src/modules/peek-view/view/peek-view-controls.css.ts
index 38334d2c36..1bc13e69cb 100644
--- a/packages/frontend/core/src/modules/peek-view/view/peek-view-controls.css.ts
+++ b/packages/frontend/core/src/modules/peek-view/view/peek-view-controls.css.ts
@@ -1,4 +1,3 @@
-import { cssVar } from '@toeverything/theme';
import { style } from '@vanilla-extract/css';
export const root = style({
@@ -9,12 +8,7 @@ export const root = style({
});
export const button = style({
- color: cssVar('iconColor'),
- boxShadow: cssVar('shadow2'),
borderRadius: 8,
- fontSize: '20px !important',
- ':hover': {
- background: cssVar('hoverColorFilled'),
- },
- pointerEvents: 'auto',
+ width: 32,
+ height: 32,
});
diff --git a/packages/frontend/core/src/modules/peek-view/view/peek-view-controls.tsx b/packages/frontend/core/src/modules/peek-view/view/peek-view-controls.tsx
index da62eb2fef..b06d8585bf 100644
--- a/packages/frontend/core/src/modules/peek-view/view/peek-view-controls.tsx
+++ b/packages/frontend/core/src/modules/peek-view/view/peek-view-controls.tsx
@@ -1,4 +1,4 @@
-import { IconButton, Tooltip } from '@affine/component';
+import { IconButton } from '@affine/component';
import { useNavigateHelper } from '@affine/core/hooks/use-navigate-helper';
import { useI18n } from '@affine/i18n';
import {
@@ -45,18 +45,16 @@ export const ControlButton = ({
);
return (
-
-
-
+
);
};
diff --git a/packages/frontend/core/src/modules/tag/view/delete-tag-modal.tsx b/packages/frontend/core/src/modules/tag/view/delete-tag-modal.tsx
index 2fed54a989..c607a30c8b 100644
--- a/packages/frontend/core/src/modules/tag/view/delete-tag-modal.tsx
+++ b/packages/frontend/core/src/modules/tag/view/delete-tag-modal.tsx
@@ -56,7 +56,7 @@ export const DeleteTagConfirmModal = ({
}
confirmText={t['Delete']()}
confirmButtonOptions={{
- type: 'warning',
+ variant: 'error',
}}
onConfirm={handleDelete}
/>
diff --git a/packages/frontend/core/src/modules/workbench/view/route-container.tsx b/packages/frontend/core/src/modules/workbench/view/route-container.tsx
index 717b342513..dc230badc1 100644
--- a/packages/frontend/core/src/modules/workbench/view/route-container.tsx
+++ b/packages/frontend/core/src/modules/workbench/view/route-container.tsx
@@ -30,7 +30,7 @@ const ToggleButton = ({
}) => {
return (
{
[view]
);
- return (
+ return tabItems.length ? (
{
onChange={handleActiveTabChange}
activeItemStyle={{ color: cssVar('primaryColor') }}
/>
- );
+ ) : null;
};
diff --git a/packages/frontend/core/src/modules/workbench/view/sidebar/sidebar-header.tsx b/packages/frontend/core/src/modules/workbench/view/sidebar/sidebar-header.tsx
index a053249336..76cadb00a6 100644
--- a/packages/frontend/core/src/modules/workbench/view/sidebar/sidebar-header.tsx
+++ b/packages/frontend/core/src/modules/workbench/view/sidebar/sidebar-header.tsx
@@ -34,7 +34,7 @@ function Container({
const ToggleButton = ({ onToggle }: { onToggle?: () => void }) => {
return (
-
+
);
diff --git a/packages/frontend/core/src/pages/expired.tsx b/packages/frontend/core/src/pages/expired.tsx
index 2aa98427f9..36211e1e35 100644
--- a/packages/frontend/core/src/pages/expired.tsx
+++ b/packages/frontend/core/src/pages/expired.tsx
@@ -17,7 +17,7 @@ export const Component = () => {
title={t['com.affine.expired.page.title']()}
subtitle={t['com.affine.expired.page.subtitle']()}
>
-
+
{t['com.affine.auth.open.affine']()}
diff --git a/packages/frontend/core/src/pages/subscribe.tsx b/packages/frontend/core/src/pages/subscribe.tsx
index 41dd662056..f3684bbd6d 100644
--- a/packages/frontend/core/src/pages/subscribe.tsx
+++ b/packages/frontend/core/src/pages/subscribe.tsx
@@ -154,7 +154,7 @@ export const Component = () => {
<>
{error}
- setRetryKey(i => i + 1)}>
+ setRetryKey(i => i + 1)}>
Retry
>
diff --git a/packages/frontend/core/src/pages/workspace/all-collection/header.css.ts b/packages/frontend/core/src/pages/workspace/all-collection/header.css.ts
index 9b1b795a32..75bb3b1ced 100644
--- a/packages/frontend/core/src/pages/workspace/all-collection/header.css.ts
+++ b/packages/frontend/core/src/pages/workspace/all-collection/header.css.ts
@@ -1,12 +1,10 @@
import { style } from '@vanilla-extract/css';
export const headerCreateNewCollectionIconButton = style({
- padding: '4px 8px',
- fontSize: '16px',
width: '32px',
- height: '28px',
- borderRadius: '8px',
- transition: 'opacity 0.1s ease-in-out',
+ height: '32px',
+ borderRadius: 8,
+ transition: 'all 0.1s ease-in-out',
});
export const headerCreateNewButtonHidden = style({
opacity: 0,
diff --git a/packages/frontend/core/src/pages/workspace/all-collection/header.tsx b/packages/frontend/core/src/pages/workspace/all-collection/header.tsx
index bb90a65999..82dc49b7a2 100644
--- a/packages/frontend/core/src/pages/workspace/all-collection/header.tsx
+++ b/packages/frontend/core/src/pages/workspace/all-collection/header.tsx
@@ -17,8 +17,8 @@ export const AllCollectionHeader = ({
}
+ size="16"
+ icon={}
onClick={onCreateCollection}
className={clsx(
styles.headerCreateNewCollectionIconButton,
diff --git a/packages/frontend/core/src/pages/workspace/collection/collection.css.ts b/packages/frontend/core/src/pages/workspace/collection/collection.css.ts
index 8daf9aa86a..dc64c14939 100644
--- a/packages/frontend/core/src/pages/workspace/collection/collection.css.ts
+++ b/packages/frontend/core/src/pages/workspace/collection/collection.css.ts
@@ -27,10 +27,8 @@ export const headerCreateNewButton = style({
transition: 'opacity 0.1s ease-in-out',
});
export const headerCreateNewCollectionIconButton = style({
- padding: '4px 8px',
- fontSize: '16px',
- width: '32px',
- height: '28px',
+ width: '30px',
+ height: '30px',
borderRadius: '8px',
});
export const headerCreateNewButtonHidden = style({
diff --git a/packages/frontend/core/src/pages/workspace/collection/header.tsx b/packages/frontend/core/src/pages/workspace/collection/header.tsx
index 2249c1337c..41580d9acb 100644
--- a/packages/frontend/core/src/pages/workspace/collection/header.tsx
+++ b/packages/frontend/core/src/pages/workspace/collection/header.tsx
@@ -19,8 +19,8 @@ export const CollectionDetailHeader = ({
right={
<>
}
+ size="16"
+ icon={}
onClick={onCreate}
className={clsx(
styles.headerCreateNewButton,
diff --git a/packages/frontend/core/src/pages/workspace/detail-page/tabs/journal.tsx b/packages/frontend/core/src/pages/workspace/detail-page/tabs/journal.tsx
index b4664d1e01..c0ad2ca3bd 100644
--- a/packages/frontend/core/src/pages/workspace/detail-page/tabs/journal.tsx
+++ b/packages/frontend/core/src/pages/workspace/detail-page/tabs/journal.tsx
@@ -330,7 +330,7 @@ const ConflictList = ({
/>
}
>
-
+
diff --git a/tests/affine-local/e2e/quick-search.spec.ts b/tests/affine-local/e2e/quick-search.spec.ts
index 89e67d4d26..3ecf556430 100644
--- a/tests/affine-local/e2e/quick-search.spec.ts
+++ b/tests/affine-local/e2e/quick-search.spec.ts
@@ -556,8 +556,10 @@ test('can use slash menu to insert a newly created doc card', async ({
page,
}) => {
await openHomePage(page);
- await clickNewPageButton(page);
+ // title '1' is a workaround to make sure Keyboard enter works correctly
+ await clickNewPageButton(page, '1');
+ // flaky: still focus on the title input
// goto main content
await page.keyboard.press('Enter');