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 (