diff --git a/packages/frontend/component/package.json b/packages/frontend/component/package.json index bd7d5bcee3..ec34e080c8 100644 --- a/packages/frontend/component/package.json +++ b/packages/frontend/component/package.json @@ -43,12 +43,13 @@ "@radix-ui/react-popover": "^1.0.7", "@radix-ui/react-radio-group": "^1.1.3", "@radix-ui/react-scroll-area": "^1.0.5", + "@radix-ui/react-slot": "^1.1.0", "@radix-ui/react-tabs": "^1.1.0", "@radix-ui/react-toast": "^1.1.5", "@radix-ui/react-toolbar": "^1.0.4", "@radix-ui/react-tooltip": "^1.0.7", "@radix-ui/react-visually-hidden": "^1.1.0", - "@toeverything/theme": "^1.0.4", + "@toeverything/theme": "^1.0.5", "@vanilla-extract/dynamic": "^2.1.0", "bytes": "^3.1.2", "check-password-strength": "^2.0.10", diff --git a/packages/frontend/component/src/theme/global.css b/packages/frontend/component/src/theme/global.css index ab05eda9a2..eaab43b367 100644 --- a/packages/frontend/component/src/theme/global.css +++ b/packages/frontend/component/src/theme/global.css @@ -288,3 +288,9 @@ body { [data-lit-react-wrapper] { display: contents; } + +/* Avoid color overriden by user-agent */ +button, +input { + color: inherit; +} diff --git a/packages/frontend/component/src/ui/button/styles.css.ts b/packages/frontend/component/src/ui/button/styles.css.ts index 74aa1b11fb..43d7432e9f 100644 --- a/packages/frontend/component/src/ui/button/styles.css.ts +++ b/packages/frontend/component/src/ui/button/styles.css.ts @@ -1,6 +1,5 @@ import { cssVar } from '@toeverything/theme'; -import { cssVarV2 } from '@toeverything/theme/v2'; -import { globalStyle, style } from '@vanilla-extract/css'; +import { style } from '@vanilla-extract/css'; export const dropdownBtn = style({ display: 'inline-flex', alignItems: 'center', @@ -108,363 +107,3 @@ export const radioButtonGroup = style({ // @ts-expect-error - fix electron drag WebkitAppRegion: 'no-drag', }); -export const button = style({ - display: 'inline-flex', - justifyContent: 'center', - alignItems: 'center', - userSelect: 'none', - touchAction: 'manipulation', - outline: '0', - border: '1px solid', - padding: '0 18px', - borderRadius: '8px', - fontSize: cssVar('fontBase'), - transition: 'all .3s', - ['WebkitAppRegion' as string]: 'no-drag', - fontWeight: 600, - // changeable - height: '28px', - background: cssVar('white'), - borderColor: cssVarV2('layer/insideBorder/border'), - color: cssVarV2('text/primary'), - 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', - }, - '&.block': { - display: 'flex', - width: '100%', - }, - '&.circle': { - borderRadius: '50%', - }, - '&.round': { - borderRadius: '14px', - }, - // size - '&.large': { - height: '32px', - }, - '&.round.large': { - borderRadius: '16px', - }, - '&.extraLarge': { - height: '40px', - }, - '&.round.extraLarge': { - borderRadius: '20px', - }, - // type - '&.plain': { - color: cssVar('textPrimaryColor'), - borderColor: 'transparent', - background: 'transparent', - }, - '&.primary': { - color: cssVar('pureWhite'), - background: cssVar('primaryColor'), - borderColor: cssVar('black10'), - boxShadow: cssVar('buttonInnerShadow'), - }, - '&.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'), - boxShadow: cssVar('buttonInnerShadow'), - }, - '&.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'), - boxShadow: cssVar('buttonInnerShadow'), - }, - '&.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'), - boxShadow: cssVar('buttonInnerShadow'), - }, - '&.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'), - boxShadow: cssVar('buttonInnerShadow'), - }, - '&.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'), - }, - }, -}); -globalStyle(`${button} > span`, { - // flex: 1, - lineHeight: 1, - padding: '0 4px', -}); -export const buttonIcon = 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('white'), - }, - }, -}); -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', - // changeable - width: '24px', - height: '24px', - fontSize: '20px', - color: cssVar('textPrimaryColor'), - borderColor: cssVar('borderColor'), - 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', - }, - // size - '&.large': { - width: '32px', - height: '32px', - fontSize: '24px', - }, - '&.large.without-padding': { - margin: '-4px', - }, - '&.small': { - width: '20px', - height: '20px', - fontSize: '16px', - }, - '&.extra-small': { - width: '16px', - height: '16px', - fontSize: '12px', - }, - // type - '&.plain': { - color: cssVar('iconColor'), - borderColor: 'transparent', - background: 'transparent', - }, - '&.plain.active': { - color: cssVar('primaryColor'), - }, - '&.primary': { - color: cssVar('white'), - background: cssVar('primaryColor'), - borderColor: cssVar('black10'), - boxShadow: '0px 1px 2px 0px rgba(255, 255, 255, 0.25) inset', - }, - '&.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'), - boxShadow: '0px 1px 2px 0px rgba(255, 255, 255, 0.25) inset', - }, - '&.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'), - boxShadow: '0px 1px 2px 0px rgba(255, 255, 255, 0.25) inset', - }, - '&.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'), - boxShadow: '0px 1px 2px 0px rgba(255, 255, 255, 0.25) inset', - }, - '&.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'), - boxShadow: '0px 1px 2px 0px rgba(255, 255, 255, 0.25) inset', - }, - '&.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'), - }, - }, -}); diff --git a/packages/frontend/component/src/ui/menu/desktop/item.tsx b/packages/frontend/component/src/ui/menu/desktop/item.tsx new file mode 100644 index 0000000000..4d32afed2f --- /dev/null +++ b/packages/frontend/component/src/ui/menu/desktop/item.tsx @@ -0,0 +1,13 @@ +import * as DropdownMenu from '@radix-ui/react-dropdown-menu'; + +import type { MenuItemProps } from '../menu.types'; +import { useMenuItem } from '../use-menu-item'; + +export const DesktopMenuItem = (props: MenuItemProps) => { + const { className, children, otherProps } = useMenuItem(props); + return ( + + {children} + + ); +}; diff --git a/packages/frontend/component/src/ui/menu/desktop/root.tsx b/packages/frontend/component/src/ui/menu/desktop/root.tsx new file mode 100644 index 0000000000..aa8c77c6f6 --- /dev/null +++ b/packages/frontend/component/src/ui/menu/desktop/root.tsx @@ -0,0 +1,39 @@ +import * as DropdownMenu from '@radix-ui/react-dropdown-menu'; +import clsx from 'clsx'; +import { Fragment } from 'react'; + +import type { MenuProps } from '../menu.types'; +import * as styles from '../styles.css'; + +export const DesktopMenu = ({ + children, + items, + portalOptions, + rootOptions, + noPortal, + contentOptions: { + className = '', + style: contentStyle = {}, + ...otherContentOptions + } = {}, +}: MenuProps) => { + const Wrapper = noPortal ? Fragment : DropdownMenu.Portal; + const wrapperProps = noPortal ? {} : portalOptions; + return ( + + {children} + + + + {items} + + + + ); +}; diff --git a/packages/frontend/component/src/ui/menu/menu-separator.tsx b/packages/frontend/component/src/ui/menu/desktop/separator.tsx similarity index 59% rename from packages/frontend/component/src/ui/menu/menu-separator.tsx rename to packages/frontend/component/src/ui/menu/desktop/separator.tsx index d5f746edcc..1238471332 100644 --- a/packages/frontend/component/src/ui/menu/menu-separator.tsx +++ b/packages/frontend/component/src/ui/menu/desktop/separator.tsx @@ -1,20 +1,16 @@ import type { DropdownMenuSeparatorProps } from '@radix-ui/react-dropdown-menu'; import * as DropdownMenu from '@radix-ui/react-dropdown-menu'; import clsx from 'clsx'; -import { useMemo } from 'react'; -import * as styles from './styles.css'; +import * as styles from '../styles.css'; -export const MenuSeparator = ({ +export const DesktopMenuSeparator = ({ className, ...otherProps }: DropdownMenuSeparatorProps) => { return ( clsx(styles.menuSeparator, className), - [className] - )} + className={clsx(styles.menuSeparator, className)} {...otherProps} /> ); diff --git a/packages/frontend/component/src/ui/menu/desktop/sub.tsx b/packages/frontend/component/src/ui/menu/desktop/sub.tsx new file mode 100644 index 0000000000..2d2909a1c1 --- /dev/null +++ b/packages/frontend/component/src/ui/menu/desktop/sub.tsx @@ -0,0 +1,46 @@ +import { ArrowRightSmallIcon } from '@blocksuite/icons/rc'; +import * as DropdownMenu from '@radix-ui/react-dropdown-menu'; +import clsx from 'clsx'; +import { useMemo } from 'react'; + +import type { MenuSubProps } from '../menu.types'; +import * as styles from '../styles.css'; +import { useMenuItem } from '../use-menu-item'; + +export const DesktopMenuSub = ({ + children: propsChildren, + items, + portalOptions, + subOptions, + triggerOptions, + subContentOptions: { + className: subContentClassName = '', + ...otherSubContentOptions + } = {}, +}: MenuSubProps) => { + const { className, children, otherProps } = useMenuItem({ + ...triggerOptions, + children: propsChildren, + suffixIcon: , + }); + + return ( + + + {children} + + + clsx(styles.menuContent, subContentClassName), + [subContentClassName] + )} + {...otherSubContentOptions} + > + {items} + + + + ); +}; diff --git a/packages/frontend/component/src/ui/menu/index.ts b/packages/frontend/component/src/ui/menu/index.ts index 405ef1e04c..2345af9ddd 100644 --- a/packages/frontend/component/src/ui/menu/index.ts +++ b/packages/frontend/component/src/ui/menu/index.ts @@ -1,7 +1,29 @@ -export * from './menu'; export * from './menu.types'; -export * from './menu-icon'; -export * from './menu-item'; -export * from './menu-separator'; -export * from './menu-sub'; -export * from './menu-trigger'; +import { isMobile } from '../../utils/env'; +import { DesktopMenuItem } from './desktop/item'; +import { DesktopMenu } from './desktop/root'; +import { DesktopMenuSeparator } from './desktop/separator'; +import { DesktopMenuSub } from './desktop/sub'; +import { MenuTrigger } from './menu-trigger'; +import { MobileMenuItem } from './mobile/item'; +import { MobileMenu } from './mobile/root'; +import { MobileMenuSeparator } from './mobile/separator'; +import { MobileMenuSub } from './mobile/sub'; + +const MenuItem = isMobile() ? MobileMenuItem : DesktopMenuItem; +const MenuSeparator = isMobile() ? MobileMenuSeparator : DesktopMenuSeparator; +const MenuSub = isMobile() ? MobileMenuSub : DesktopMenuSub; +const Menu = isMobile() ? MobileMenu : DesktopMenu; + +export { + DesktopMenu, + DesktopMenuItem, + DesktopMenuSeparator, + DesktopMenuSub, + MobileMenu, + MobileMenuItem, + MobileMenuSeparator, + MobileMenuSub, +}; + +export { Menu, MenuItem, MenuSeparator, MenuSub, MenuTrigger }; diff --git a/packages/frontend/component/src/ui/menu/menu-icon.tsx b/packages/frontend/component/src/ui/menu/menu-icon.tsx deleted file mode 100644 index 60f057a9b7..0000000000 --- a/packages/frontend/component/src/ui/menu/menu-icon.tsx +++ /dev/null @@ -1,39 +0,0 @@ -import clsx from 'clsx'; -import type { HTMLAttributes, PropsWithChildren, ReactNode } from 'react'; -import { forwardRef, useMemo } from 'react'; - -import { menuItemIcon } from './styles.css'; - -export interface MenuIconProps - extends PropsWithChildren, - HTMLAttributes { - icon?: ReactNode; - position?: 'start' | 'end'; -} - -export const MenuIcon = forwardRef( - ({ children, icon, position = 'start', className, ...otherProps }, ref) => { - return ( -
- clsx( - menuItemIcon, - { - end: position === 'end', - start: position === 'start', - }, - className - ), - [className, position] - )} - {...otherProps} - > - {icon || children} -
- ); - } -); - -MenuIcon.displayName = 'MenuIcon'; diff --git a/packages/frontend/component/src/ui/menu/menu-item.tsx b/packages/frontend/component/src/ui/menu/menu-item.tsx deleted file mode 100644 index a6278f9648..0000000000 --- a/packages/frontend/component/src/ui/menu/menu-item.tsx +++ /dev/null @@ -1,33 +0,0 @@ -import * as DropdownMenu from '@radix-ui/react-dropdown-menu'; - -import type { MenuItemProps } from './menu.types'; -import { useMenuItem } from './use-menu-item'; - -export const MenuItem = ({ - children: propsChildren, - type = 'default', - className: propsClassName, - preFix, - endFix, - checked, - selected, - block, - ...otherProps -}: MenuItemProps) => { - const { className, children } = useMenuItem({ - children: propsChildren, - className: propsClassName, - type, - preFix, - endFix, - checked, - selected, - block, - }); - - return ( - - {children} - - ); -}; diff --git a/packages/frontend/component/src/ui/menu/menu-sub.tsx b/packages/frontend/component/src/ui/menu/menu-sub.tsx deleted file mode 100644 index 659fc203d9..0000000000 --- a/packages/frontend/component/src/ui/menu/menu-sub.tsx +++ /dev/null @@ -1,72 +0,0 @@ -import { ArrowRightSmallIcon } from '@blocksuite/icons/rc'; -import type { - DropdownMenuPortalProps, - DropdownMenuSubContentProps, - DropdownMenuSubProps, -} from '@radix-ui/react-dropdown-menu'; -import * as DropdownMenu from '@radix-ui/react-dropdown-menu'; -import clsx from 'clsx'; -import type { ReactNode } from 'react'; -import { useMemo } from 'react'; - -import type { MenuItemProps } from './menu.types'; -import { MenuIcon } from './menu-icon'; -import * as styles from './styles.css'; -import { useMenuItem } from './use-menu-item'; -export interface MenuSubProps { - children: ReactNode; - items: ReactNode; - triggerOptions?: Omit; - portalOptions?: Omit; - subOptions?: Omit; - subContentOptions?: Omit; -} - -export const MenuSub = ({ - children: propsChildren, - items, - portalOptions, - subOptions, - triggerOptions: { - className: propsClassName, - preFix, - endFix, - type, - ...otherTriggerOptions - } = {}, - subContentOptions: { - className: subContentClassName = '', - ...otherSubContentOptions - } = {}, -}: MenuSubProps) => { - const { className, children } = useMenuItem({ - children: propsChildren, - className: propsClassName, - type, - preFix, - endFix, - }); - - return ( - - - {children} - - - - - - clsx(styles.menuContent, subContentClassName), - [subContentClassName] - )} - {...otherSubContentOptions} - > - {items} - - - - ); -}; diff --git a/packages/frontend/component/src/ui/menu/menu-trigger.stories.tsx b/packages/frontend/component/src/ui/menu/menu-trigger.stories.tsx deleted file mode 100644 index 7aebf22368..0000000000 --- a/packages/frontend/component/src/ui/menu/menu-trigger.stories.tsx +++ /dev/null @@ -1,18 +0,0 @@ -import type { Meta, StoryFn } from '@storybook/react'; - -import type { MenuTriggerProps } from './index'; -import { MenuTrigger } from './index'; - -export default { - title: 'UI/MenuTrigger', - component: MenuTrigger, -} satisfies Meta; - -const Template: StoryFn = args => ( -
- This is a menu trigger -
-); - -export const Default: StoryFn = Template.bind(undefined); -Default.args = {}; diff --git a/packages/frontend/component/src/ui/menu/menu-trigger.tsx b/packages/frontend/component/src/ui/menu/menu-trigger.tsx index 61856005d8..e8ae10b20e 100644 --- a/packages/frontend/component/src/ui/menu/menu-trigger.tsx +++ b/packages/frontend/component/src/ui/menu/menu-trigger.tsx @@ -1,87 +1,24 @@ import { ArrowDownSmallIcon } from '@blocksuite/icons/rc'; -import { assignInlineVars } from '@vanilla-extract/dynamic'; import clsx from 'clsx'; -import type { - CSSProperties, - HTMLAttributes, - PropsWithChildren, - ReactNode, -} from 'react'; -import { forwardRef } from 'react'; +import { forwardRef, type Ref } from 'react'; -import { MenuIcon } from './menu-icon'; -import * as styles from './styles.css'; -import { triggerWidthVar } from './styles.css'; +import { Button, type ButtonProps } from '../button'; -export interface MenuTriggerProps - extends PropsWithChildren, - HTMLAttributes { - width?: CSSProperties['width']; - disabled?: boolean; - noBorder?: boolean; - status?: 'error' | 'success' | 'warning' | 'default'; - size?: 'default' | 'large' | 'extraLarge'; - preFix?: ReactNode; - endFix?: ReactNode; - block?: boolean; -} +export interface MenuTriggerProps extends ButtonProps {} -export const MenuTrigger = forwardRef( - ( - { - disabled, - noBorder = false, - className, - status = 'default', - size = 'default', - preFix, - endFix, - block = false, - children, - width, - style = {}, - ...otherProps - }, - ref - ) => { - return ( - - ); - } -); - -MenuTrigger.displayName = 'MenuTrigger'; +export const MenuTrigger = forwardRef(function MenuTrigger( + { children, className, contentStyle, ...otherProps }: MenuTriggerProps, + ref: Ref +) { + return ( + + ); +}); diff --git a/packages/frontend/component/src/ui/menu/menu.stories.tsx b/packages/frontend/component/src/ui/menu/menu.stories.tsx index 90a7ffcf2b..7ad492d7cd 100644 --- a/packages/frontend/component/src/ui/menu/menu.stories.tsx +++ b/packages/frontend/component/src/ui/menu/menu.stories.tsx @@ -6,14 +6,7 @@ import { useCallback, useState } from 'react'; import { Button } from '../button'; import { Tooltip } from '../tooltip'; import type { MenuItemProps, MenuProps } from './index'; -import { - Menu, - MenuIcon, - MenuItem, - MenuSeparator, - MenuSub, - MenuTrigger, -} from './index'; +import { Menu, MenuItem, MenuSeparator, MenuSub } from './index'; export default { title: 'UI/Menu', @@ -29,14 +22,14 @@ const Template: StoryFn = args => ( }, }} > - menu trigger + ); interface Items { label: ReactNode; type?: MenuItemProps['type']; - preFix?: MenuItemProps['preFix']; + prefixIcon?: MenuItemProps['prefixIcon']; disabled?: boolean; divider?: boolean; subItems?: Items[]; @@ -49,13 +42,7 @@ const items: Items[] = [ }, { label: 'menu item with icon', - preFix: ( - - - - - - ), + prefixIcon: , }, { label: ( @@ -80,13 +67,7 @@ const items: Items[] = [ label: 'danger menu item', type: 'danger', block: true, - preFix: ( - - - - - - ), + prefixIcon: , }, { label: 'warning menu item', @@ -121,6 +102,9 @@ const items: Items[] = [ { label: 'sub menu item 2-2', }, + { + label: 'sub menu item 2-3', + }, ], }, ], diff --git a/packages/frontend/component/src/ui/menu/menu.tsx b/packages/frontend/component/src/ui/menu/menu.tsx deleted file mode 100644 index 1f9a091d02..0000000000 --- a/packages/frontend/component/src/ui/menu/menu.tsx +++ /dev/null @@ -1,62 +0,0 @@ -import type { - DropdownMenuContentProps, - DropdownMenuPortalProps, - DropdownMenuProps, -} from '@radix-ui/react-dropdown-menu'; -import * as DropdownMenu from '@radix-ui/react-dropdown-menu'; -import clsx from 'clsx'; -import type { ReactNode } from 'react'; - -import * as styles from './styles.css'; - -export interface MenuProps { - children: ReactNode; - items: ReactNode; - portalOptions?: Omit; - rootOptions?: Omit; - contentOptions?: Omit; - noPortal?: boolean; -} - -export const Menu = ({ - children, - items, - portalOptions, - rootOptions, - noPortal, - contentOptions: { - className = '', - style: contentStyle = {}, - ...otherContentOptions - } = {}, -}: MenuProps) => { - return ( - - {children} - - {noPortal ? ( - - {items} - - ) : ( - - - {items} - - - )} - - ); -}; diff --git a/packages/frontend/component/src/ui/menu/menu.types.ts b/packages/frontend/component/src/ui/menu/menu.types.ts index a47ce91b76..813d2d669a 100644 --- a/packages/frontend/component/src/ui/menu/menu.types.ts +++ b/packages/frontend/component/src/ui/menu/menu.types.ts @@ -1,11 +1,40 @@ -import type { DropdownMenuItemProps as MenuItemPropsPrimitive } from '@radix-ui/react-dropdown-menu'; +import type { + DropdownMenuContentProps, + DropdownMenuItemProps as MenuItemPropsPrimitive, + DropdownMenuPortalProps, + DropdownMenuProps, + DropdownMenuSubContentProps, + DropdownMenuSubProps, +} from '@radix-ui/react-dropdown-menu'; +import type { ReactNode } from 'react'; + +export interface MenuProps { + children: ReactNode; + items: ReactNode; + portalOptions?: Omit; + rootOptions?: Omit; + contentOptions?: Omit; + noPortal?: boolean; +} export interface MenuItemProps - extends Omit { + extends Omit { type?: 'default' | 'warning' | 'danger'; - preFix?: React.ReactNode; - endFix?: React.ReactNode; + // preFix?: React.ReactNode; + // endFix?: React.ReactNode; + prefix?: ReactNode; + suffix?: ReactNode; + prefixIcon?: ReactNode; + suffixIcon?: ReactNode; checked?: boolean; selected?: boolean; block?: boolean; } +export interface MenuSubProps { + children: ReactNode; + items: ReactNode; + triggerOptions?: Omit; + portalOptions?: Omit; + subOptions?: Omit; + subContentOptions?: Omit; +} diff --git a/packages/frontend/component/src/ui/menu/mobile/context.ts b/packages/frontend/component/src/ui/menu/mobile/context.ts new file mode 100644 index 0000000000..ec91487a5f --- /dev/null +++ b/packages/frontend/component/src/ui/menu/mobile/context.ts @@ -0,0 +1,22 @@ +import { + createContext, + type Dispatch, + type ReactNode, + type SetStateAction, +} from 'react'; + +import type { MenuSubProps } from '../menu.types'; + +export type SubMenuContent = { + items: ReactNode; + contentOptions?: MenuSubProps['subContentOptions']; +}; + +export const MobileMenuContext = createContext<{ + subMenus: Array; + setSubMenus: Dispatch>>; + setOpen?: (v: boolean) => void; +}>({ + subMenus: [], + setSubMenus: () => {}, +}); diff --git a/packages/frontend/component/src/ui/menu/mobile/item.tsx b/packages/frontend/component/src/ui/menu/mobile/item.tsx new file mode 100644 index 0000000000..6c807ae123 --- /dev/null +++ b/packages/frontend/component/src/ui/menu/mobile/item.tsx @@ -0,0 +1,35 @@ +import { useCallback, useContext } from 'react'; + +import type { MenuItemProps } from '../menu.types'; +import { useMenuItem } from '../use-menu-item'; +import { MobileMenuContext } from './context'; + +let preventDefaultFlag = false; +const preventDefault = () => { + preventDefaultFlag = true; +}; + +export const MobileMenuItem = (props: MenuItemProps) => { + const { setOpen } = useContext(MobileMenuContext); + const { className, children, otherProps } = useMenuItem(props); + const { onSelect, onClick, ...restProps } = otherProps; + + const onItemClick = useCallback( + (e: any) => { + onSelect?.(e); + onClick?.({ ...e, preventDefault }); + if (preventDefaultFlag) { + preventDefaultFlag = false; + } else { + setOpen?.(false); + } + }, + [onClick, onSelect, setOpen] + ); + + return ( +
+ {children} +
+ ); +}; diff --git a/packages/frontend/component/src/ui/menu/mobile/root.tsx b/packages/frontend/component/src/ui/menu/mobile/root.tsx new file mode 100644 index 0000000000..ecc3de7782 --- /dev/null +++ b/packages/frontend/component/src/ui/menu/mobile/root.tsx @@ -0,0 +1,166 @@ +import { ArrowLeftSmallIcon } from '@blocksuite/icons/rc'; +import { Slot } from '@radix-ui/react-slot'; +import clsx from 'clsx'; +import { + Fragment, + useCallback, + useContext, + useEffect, + useRef, + useState, +} from 'react'; + +import { observeResize } from '../../../utils'; +import { Button } from '../../button'; +import { Modal, type ModalProps } from '../../modal'; +import type { MenuProps } from '../menu.types'; +import type { SubMenuContent } from './context'; +import { MobileMenuContext } from './context'; +import * as styles from './styles.css'; +import { MobileMenuSubRaw } from './sub'; + +export const MobileMenu = ({ + children, + items, + noPortal, + contentOptions: { + className, + onPointerDownOutside, + // ignore the following props + sideOffset: _sideOffset, + side: _side, + align: _align, + + ...otherContentOptions + } = {}, + rootOptions, +}: MenuProps) => { + const [subMenus, setSubMenus] = useState([]); + const [open, setOpen] = useState(false); + const [sliderHeight, setSliderHeight] = useState(0); + const { setOpen: pSetOpen } = useContext(MobileMenuContext); + const finalOpen = rootOptions?.open ?? open; + const sliderRef = useRef(null); + const activeIndex = subMenus.length; + + const onOpenChange = useCallback( + (open: boolean) => { + if (!open) { + // a workaround to hack the onPointerDownOutside event + onPointerDownOutside?.({} as any); + setSubMenus([]); + } + setOpen(open); + rootOptions?.onOpenChange?.(open); + }, + [onPointerDownOutside, rootOptions] + ); + + const Wrapper = noPortal ? Fragment : Modal; + const wrapperProps = noPortal + ? {} + : ({ + open: finalOpen, + onOpenChange, + width: '100%', + animation: 'slideBottom', + withoutCloseButton: true, + contentOptions: { + className: clsx(className, styles.mobileMenuModal), + ...otherContentOptions, + }, + contentWrapperStyle: { + alignItems: 'end', + paddingBottom: 10, + }, + } satisfies ModalProps); + + const onItemClick = useCallback((e: any) => { + e.preventDefault(); + setOpen(prev => !prev); + }, []); + + // dynamic height for slider + useEffect(() => { + if (!finalOpen) return; + let observer: () => void; + const t = setTimeout(() => { + const slider = sliderRef.current; + if (!slider) return; + + const active = slider.querySelector( + `.${styles.menuContent}[data-index="${activeIndex}"]` + ); + if (!active) return; + + // for the situation that content is loaded asynchronously + observer = observeResize(active, entry => { + setSliderHeight(entry.borderBoxSize[0].blockSize); + }); + }, 0); + + return () => { + clearTimeout(t); + observer?.(); + }; + }, [activeIndex, finalOpen]); + + /** + * For cascading menu usage + * ```tsx + * Click me + * } + * > + * Root + * + * ``` + */ + if (pSetOpen) { + return {children}; + } + + return ( + <> + {children} + + +
+
+ {items} +
+ {subMenus.map((sub, index) => ( +
+ + + {sub.items} +
+ ))} +
+
+
+ + ); +}; diff --git a/packages/frontend/component/src/ui/menu/mobile/separator.tsx b/packages/frontend/component/src/ui/menu/mobile/separator.tsx new file mode 100644 index 0000000000..d205c62744 --- /dev/null +++ b/packages/frontend/component/src/ui/menu/mobile/separator.tsx @@ -0,0 +1,13 @@ +import type { DropdownMenuSeparatorProps } from '@radix-ui/react-dropdown-menu'; +import clsx from 'clsx'; + +import * as styles from '../styles.css'; + +export const MobileMenuSeparator = ({ + className, + style, +}: DropdownMenuSeparatorProps) => { + return ( +
+ ); +}; diff --git a/packages/frontend/component/src/ui/menu/mobile/styles.css.ts b/packages/frontend/component/src/ui/menu/mobile/styles.css.ts new file mode 100644 index 0000000000..edbd45ccfa --- /dev/null +++ b/packages/frontend/component/src/ui/menu/mobile/styles.css.ts @@ -0,0 +1,79 @@ +import { cssVar } from '@toeverything/theme'; +import { cssVarV2 } from '@toeverything/theme/v2'; +import { style } from '@vanilla-extract/css'; + +import { modalContent } from '../../modal/styles.css'; +import { bgColor } from '../styles.css'; + +// To override desktop menu style defined in '../styles.css.ts' + +export const mobileMenuModal = style({ + selectors: { + // to make sure it will override the desktop modal style + [`&.${modalContent}`]: { + backgroundColor: cssVarV2('layer/background/overlayPanel'), + boxShadow: cssVar('menuShadow'), + userSelect: 'none', + borderRadius: 24, + minHeight: 0, + padding: 0, + overflow: 'hidden', + }, + }, +}); + +export const slider = style({ + display: 'flex', + alignItems: 'start', + transition: 'all 0.23s', +}); + +export const menuContent = style({ + boxSizing: 'border-box', + fontSize: 17, + fontWeight: '400', + display: 'flex', + flexDirection: 'column', + gap: 0, + width: '100%', + flexShrink: 0, + padding: '13px 0px 13px 0px', +}); + +export const mobileMenuItem = style({ + padding: '10px 20px', + borderRadius: 0, + ':hover': { + vars: { + [bgColor]: 'transparent', + }, + }, + ':active': { + vars: { + [bgColor]: cssVar('hoverColor'), + }, + }, + selectors: { + '&.danger:hover': { + vars: { [bgColor]: 'transparent' }, + }, + '&.danger:active': { + vars: { [bgColor]: cssVar('backgroundErrorColor') }, + }, + '&.warning:hover': { + vars: { [bgColor]: 'transparent' }, + }, + '&.warning:active': { + vars: { [bgColor]: cssVar('backgroundWarningColor') }, + }, + }, +}); + +export const backButton = style({ + height: 42, + alignSelf: 'start', + fontWeight: 600, + fontSize: 17, + paddingLeft: 0, + marginLeft: 20, +}); diff --git a/packages/frontend/component/src/ui/menu/mobile/sub.tsx b/packages/frontend/component/src/ui/menu/mobile/sub.tsx new file mode 100644 index 0000000000..cb2c773c7b --- /dev/null +++ b/packages/frontend/component/src/ui/menu/mobile/sub.tsx @@ -0,0 +1,55 @@ +import { ArrowRightSmallPlusIcon } from '@blocksuite/icons/rc'; +import { Slot } from '@radix-ui/react-slot'; +import { type MouseEvent, useCallback, useContext } from 'react'; + +import type { MenuSubProps } from '../menu.types'; +import { useMenuItem } from '../use-menu-item'; +import { MobileMenuContext } from './context'; + +export const MobileMenuSub = ({ + children: propsChildren, + items, + triggerOptions, + subContentOptions: contentOptions = {}, +}: MenuSubProps) => { + const { + className, + children, + otherProps: { onClick, ...otherTriggerOptions }, + } = useMenuItem({ + ...triggerOptions, + children: propsChildren, + suffixIcon: , + }); + + return ( + +
+ {children} +
+
+ ); +}; + +export const MobileMenuSubRaw = ({ + onClick, + children, + items, + subContentOptions: contentOptions = {}, +}: MenuSubProps & { onClick?: (e: MouseEvent) => void }) => { + const { setSubMenus } = useContext(MobileMenuContext); + + const onItemClick = useCallback( + (e: MouseEvent) => { + onClick?.(e); + setSubMenus(prev => [...prev, { items, contentOptions }]); + }, + [contentOptions, items, onClick, setSubMenus] + ); + + return {children}; +}; diff --git a/packages/frontend/component/src/ui/menu/styles.css.ts b/packages/frontend/component/src/ui/menu/styles.css.ts index 9ab0cff526..4ad3ad462c 100644 --- a/packages/frontend/component/src/ui/menu/styles.css.ts +++ b/packages/frontend/component/src/ui/menu/styles.css.ts @@ -1,57 +1,92 @@ import { cssVar } from '@toeverything/theme'; +import { cssVarV2 } from '@toeverything/theme/v2'; import { createVar, style } from '@vanilla-extract/css'; -export const triggerWidthVar = createVar('triggerWidthVar'); + +export const iconColor = createVar('iconColor'); +export const labelColor = createVar('labelColor'); +export const bgColor = createVar('bgColor'); + export const menuContent = style({ minWidth: '180px', - color: cssVar('textPrimaryColor'), borderRadius: '8px', padding: '8px', fontSize: cssVar('fontSm'), fontWeight: '400', - backgroundColor: cssVar('backgroundOverlayPanelColor'), + backgroundColor: cssVarV2('layer/background/overlayPanel'), boxShadow: cssVar('menuShadow'), userSelect: 'none', ['WebkitAppRegion' as string]: 'no-drag', + display: 'flex', + flexDirection: 'column', + gap: 4, + selectors: { + '&.mobile': { + padding: 0, + boxShadow: 'none', + }, + }, }); + export const menuItem = style({ + vars: { + [iconColor]: cssVarV2('icon/primary'), + [labelColor]: cssVarV2('text/primary'), + [bgColor]: 'transparent', + }, + color: labelColor, + backgroundColor: bgColor, + display: 'flex', alignItems: 'center', justifyContent: 'space-between', - padding: '4px 12px', - borderRadius: '4px', + gap: 8, + padding: '4px', + borderRadius: 4, lineHeight: '22px', border: 'none', outline: 'none', cursor: 'pointer', boxSizing: 'border-box', selectors: { - '&:not(:last-of-type)': { - marginBottom: '4px', - }, '&.block': { maxWidth: '100%', }, '&[data-disabled]': { - color: cssVar('textDisableColor'), + vars: { + [iconColor]: cssVarV2('icon/disable'), + [labelColor]: cssVarV2('text/secondary'), + }, pointerEvents: 'none', - cursor: 'not-allowed', - }, - '&[data-highlighted]': { - backgroundColor: cssVar('hoverColor'), + cursor: 'default', }, '&:hover': { - backgroundColor: cssVar('hoverColor'), + vars: { + [bgColor]: cssVar('hoverColor'), + }, + outline: 'none !important', + }, + '&:focus-visible': { + outline: '1px solid ' + cssVarV2('layer/insideBorder/primaryBorder'), }, '&.danger:hover': { - color: cssVar('errorColor'), - backgroundColor: cssVar('backgroundErrorColor'), + vars: { + [iconColor]: cssVar('errorColor'), + [labelColor]: cssVar('errorColor'), + [bgColor]: cssVar('backgroundErrorColor'), + }, }, '&.warning:hover': { - color: cssVar('warningColor'), - backgroundColor: cssVar('backgroundWarningColor'), + vars: { + [iconColor]: cssVar('warningColor'), + [labelColor]: cssVar('warningColor'), + [bgColor]: cssVar('backgroundWarningColor'), + }, }, - '&.checked': { - color: cssVar('primaryColor'), + '&.checked, &.selected': { + vars: { + [iconColor]: cssVar('primaryColor'), + [labelColor]: cssVar('primaryColor'), + }, }, }, }); @@ -66,84 +101,24 @@ export const menuItemIcon = style({ display: 'flex', flexShrink: 0, fontSize: cssVar('fontH5'), - color: cssVar('iconColor'), - selectors: { - '&.start': { marginRight: '4px' }, - '&.end': { marginLeft: '4px' }, - '&.selected, &.checked': { - color: cssVar('primaryColor'), - }, - [`${menuItem}.danger:hover &`]: { - color: cssVar('errorColor'), - }, - [`${menuItem}.warning:hover &`]: { - color: cssVar('warningColor'), - }, - }, + color: iconColor, + width: 20, + height: 20, }); + export const menuSeparator = style({ - height: '1px', - backgroundColor: cssVar('borderColor'), - marginTop: '12px', - marginBottom: '8px', -}); -export const menuTrigger = style({ - vars: { - [triggerWidthVar]: 'auto', - }, - width: triggerWidthVar, - height: 28, - lineHeight: '22px', - padding: '0 10px', - color: cssVar('textPrimaryColor'), - border: '1px solid', - backgroundColor: cssVar('white'), - borderRadius: 8, - display: 'inline-flex', - justifyContent: 'space-between', - alignItems: 'center', - fontSize: cssVar('fontXs'), - cursor: 'pointer', - ['WebkitAppRegion' as string]: 'no-drag', - borderColor: cssVar('borderColor'), - outline: 'none', - selectors: { - '&:hover': { - background: cssVar('hoverColor'), - }, - '&.no-border': { - border: 'unset', - }, - '&.block': { - display: 'flex', - width: '100%', - }, - // size - '&.large': { - height: 32, - }, - '&.extra-large': { - height: 40, - fontWeight: 600, - }, - // color - '&.disabled': { - cursor: 'default', - color: cssVar('textDisableColor'), - pointerEvents: 'none', - }, - // TODO(@catsjuice): wait for design - '&.error': { - // borderColor: 'var(--affine-error-color)', - }, - '&.success': { - // borderColor: 'var(--affine-success-color)', - }, - '&.warning': { - // borderColor: 'var(--affine-warning-color)', - }, - '&.default': { - // borderColor: 'var(--affine-border-color)', - }, + width: '100%', + height: '8px', + position: 'relative', + ':after': { + content: '""', + display: 'block', + height: '1px', + width: '100%', + backgroundColor: cssVarV2('layer/insideBorder/border'), + position: 'absolute', + top: '50%', + left: 0, + transform: 'translateY(-50%) scaleY(0.5)', }, }); diff --git a/packages/frontend/component/src/ui/menu/use-menu-item.tsx b/packages/frontend/component/src/ui/menu/use-menu-item.tsx index 0ae890426d..23e0e3b9bc 100644 --- a/packages/frontend/component/src/ui/menu/use-menu-item.tsx +++ b/packages/frontend/component/src/ui/menu/use-menu-item.tsx @@ -1,73 +1,64 @@ import { DoneIcon } from '@blocksuite/icons/rc'; import clsx from 'clsx'; -import { useMemo } from 'react'; +import { isMobile } from '../../utils/env'; import type { MenuItemProps } from './menu.types'; -import { MenuIcon } from './menu-icon'; +import { mobileMenuItem } from './mobile/styles.css'; import * as styles from './styles.css'; -interface useMenuItemProps { - children: MenuItemProps['children']; - type: MenuItemProps['type']; - className: MenuItemProps['className']; - preFix: MenuItemProps['preFix']; - endFix: MenuItemProps['endFix']; - checked?: MenuItemProps['checked']; - selected?: MenuItemProps['selected']; - block?: MenuItemProps['block']; -} - -export const useMenuItem = ({ +export const useMenuItem = ({ children: propsChildren, type = 'default', className: propsClassName, - preFix, - endFix, + prefix, + prefixIcon, + suffix, + suffixIcon, checked, selected, block, -}: useMenuItemProps) => { - const className = useMemo( - () => - clsx( - styles.menuItem, - { - danger: type === 'danger', - warning: type === 'warning', - checked, - selected, - block, - }, - propsClassName - ), - [block, checked, propsClassName, selected, type] + ...otherProps +}: T) => { + const className = clsx( + styles.menuItem, + { + danger: type === 'danger', + warning: type === 'warning', + checked, + selected, + block, + [mobileMenuItem]: isMobile(), + }, + propsClassName ); - const children = useMemo( - () => ( - <> - {preFix} - {propsChildren} - {endFix} + const children = ( + <> + {prefix} - {checked || selected ? ( - - - - ) : null} - - ), - [checked, endFix, preFix, propsChildren, selected] + {prefixIcon ? ( +
{prefixIcon}
+ ) : null} + + {propsChildren} + + {suffixIcon ? ( +
{suffixIcon}
+ ) : null} + + {suffix} + + {checked || selected ? ( +
+ +
+ ) : null} + ); return { children, className, + otherProps, }; }; diff --git a/packages/frontend/component/src/ui/modal/modal.stories.tsx b/packages/frontend/component/src/ui/modal/modal.stories.tsx index 4a6b44df5f..55f8358e43 100644 --- a/packages/frontend/component/src/ui/modal/modal.stories.tsx +++ b/packages/frontend/component/src/ui/modal/modal.stories.tsx @@ -4,6 +4,7 @@ import { useCallback, useState } from 'react'; import { Button } from '../button'; import type { InputProps } from '../input'; import { Input } from '../input'; +import { RadioGroup } from '../radio'; import type { ConfirmModalProps } from './confirm-modal'; import { ConfirmModal } from './confirm-modal'; import type { ModalProps } from './modal'; @@ -105,3 +106,36 @@ export const Confirm: StoryFn = export const Overlay: StoryFn = OverlayModalTemplate.bind(undefined); + +export const Animations = () => { + const animations = ['fadeScaleTop', 'slideBottom', 'none']; + const [open, setOpen] = useState(false); + const [animation, setAnimation] = + useState('fadeScaleTop'); + + return ( +
+ + + + This is a dialog with animation: {animation} + +
+ ); +}; diff --git a/packages/frontend/component/src/ui/modal/modal.tsx b/packages/frontend/component/src/ui/modal/modal.tsx index 19a5ef4a0f..264847d2cd 100644 --- a/packages/frontend/component/src/ui/modal/modal.tsx +++ b/packages/frontend/component/src/ui/modal/modal.tsx @@ -9,9 +9,10 @@ import * as Dialog from '@radix-ui/react-dialog'; import * as VisuallyHidden from '@radix-ui/react-visually-hidden'; import { assignInlineVars } from '@vanilla-extract/dynamic'; import clsx from 'clsx'; -import type { CSSProperties } from 'react'; +import type { CSSProperties, MouseEvent } from 'react'; import { forwardRef, useCallback, useEffect, useState } from 'react'; +import { isMobile } from '../../utils/env'; import type { IconButtonProps } from '../button'; import { IconButton } from '../button'; import * as styles from './styles.css'; @@ -32,6 +33,12 @@ export interface ModalProps extends DialogProps { contentOptions?: DialogContentProps; overlayOptions?: DialogOverlayProps; closeButtonOptions?: IconButtonProps; + contentWrapperClassName?: string; + contentWrapperStyle?: CSSProperties; + /** + * @default 'fadeScaleTop' + */ + animation?: 'fadeScaleTop' | 'none' | 'slideBottom'; } type PointerDownOutsideEvent = Parameters< Exclude @@ -128,10 +135,14 @@ export const ModalInner = forwardRef( overlayOptions: { className: overlayClassName, style: overlayStyle, + onClick: onOverlayClick, ...otherOverlayOptions } = {}, closeButtonOptions, children, + contentWrapperClassName, + contentWrapperStyle, + animation = 'fadeScaleTop', ...otherProps } = props; const { className: closeButtonClassName, ...otherCloseButtonProps } = @@ -167,6 +178,18 @@ export const ModalInner = forwardRef( [onEscapeKeyDown, persistent] ); + const handleOverlayClick = useCallback( + (e: MouseEvent) => { + onOverlayClick?.(e); + if (persistent) { + e.preventDefault(); + } else { + onOpenChange?.(false); + } + }, + [onOpenChange, onOverlayClick, persistent] + ); + if (!container) { return; } @@ -180,13 +203,27 @@ export const ModalInner = forwardRef( > -
+
{ + return environment.isBrowser && environment.isMobile; +}; diff --git a/packages/frontend/core/package.json b/packages/frontend/core/package.json index 62d3a8a1e5..2b1ae3b470 100644 --- a/packages/frontend/core/package.json +++ b/packages/frontend/core/package.json @@ -48,7 +48,7 @@ "@sentry/integrations": "^7.109.0", "@sentry/react": "^8.0.0", "@sgtpooki/file-type": "^1.0.1", - "@toeverything/theme": "^1.0.4", + "@toeverything/theme": "^1.0.5", "@vanilla-extract/dynamic": "^2.1.0", "animejs": "^3.2.2", "async-call-rpc": "^6.4.2", diff --git a/packages/frontend/core/src/components/affine/language-menu/index.tsx b/packages/frontend/core/src/components/affine/language-menu/index.tsx index 23176d26f7..cfd6d32e18 100644 --- a/packages/frontend/core/src/components/affine/language-menu/index.tsx +++ b/packages/frontend/core/src/components/affine/language-menu/index.tsx @@ -20,7 +20,7 @@ const LanguageMenuContent = memo(function LanguageMenuContent() { title={option.name} lang={option.tag} onSelect={() => onLanguageChange(option.tag)} - endFix={(option.Completeness * 100).toFixed(0) + '%'} + suffix={(option.Completeness * 100).toFixed(0) + '%'} data-selected={selected} className={styles.menuItem} > diff --git a/packages/frontend/core/src/components/affine/page-properties/menu-items.tsx b/packages/frontend/core/src/components/affine/page-properties/menu-items.tsx index b126f41319..0de30bb117 100644 --- a/packages/frontend/core/src/components/affine/page-properties/menu-items.tsx +++ b/packages/frontend/core/src/components/affine/page-properties/menu-items.tsx @@ -1,11 +1,5 @@ import type { MenuItemProps } from '@affine/component'; -import { - Input, - MenuIcon, - MenuItem, - MenuSeparator, - Scrollable, -} from '@affine/component'; +import { Input, MenuItem, MenuSeparator, Scrollable } from '@affine/component'; import type { PageInfoCustomPropertyMeta } from '@affine/core/modules/properties/services/schema'; import { useI18n } from '@affine/i18n'; import type { KeyboardEventHandler, MouseEventHandler } from 'react'; @@ -61,7 +55,7 @@ export const renderMenuItemOptions = (options: MenuItemOption[]) => { type={type} selected={selected} checked={checked} - preFix={icon ? {icon} : null} + prefixIcon={icon} onClick={onClick} > {text} diff --git a/packages/frontend/core/src/components/affine/page-properties/table.tsx b/packages/frontend/core/src/components/affine/page-properties/table.tsx index 110eabb20d..5c3d6b5419 100644 --- a/packages/frontend/core/src/components/affine/page-properties/table.tsx +++ b/packages/frontend/core/src/components/affine/page-properties/table.tsx @@ -1,12 +1,5 @@ import type { MenuProps } from '@affine/component'; -import { - Button, - IconButton, - Menu, - MenuIcon, - MenuItem, - Tooltip, -} from '@affine/component'; +import { Button, IconButton, Menu, MenuItem, Tooltip } from '@affine/component'; import { useCurrentWorkspacePropertiesAdapter } from '@affine/core/hooks/use-affine-adapter'; import { track } from '@affine/core/mixpanel'; import { DocLinksService } from '@affine/core/modules/doc-link'; @@ -346,9 +339,7 @@ export const PagePropertiesSettingsPopup = ({ className={styles.propertySettingRow} data-testid="page-properties-settings-menu-item" > - - - +
{ }} items={ <> - - } - onSelect={onClickDisable} - className={styles.menuItemStyle} - > + } onSelect={onClickDisable}>
{t['com.affine.share-menu.option.link.no-access']()} @@ -224,10 +218,7 @@ export const AFFiNESharePage = (props: ShareMenuProps) => {
- } - className={styles.menuItemStyle} + prefixIcon={} onSelect={onClickAnyoneReadOnlyShare} data-testid="share-link-menu-enable-share" > @@ -295,7 +286,6 @@ export const AFFiNESharePage = (props: ShareMenuProps) => { {isMac ? '⌘ + ⌥ + C' : 'Ctrl + Shift + C'} - {t['com.affine.share-menu.copy']()} { items={ <> - } - className={styles.menuItemStyle} + prefixIcon={} onSelect={onCopyPageLink} data-testid="share-link-menu-copy-page" > {t['com.affine.share-menu.copy.page']()} - } - className={styles.menuItemStyle} + prefixIcon={} onSelect={onCopyEdgelessLink} data-testid="share-link-menu-copy-edgeless" > {t['com.affine.share-menu.copy.edgeless']()} - - } - className={styles.menuItemStyle} - onSelect={onCopyBlockLink} - > + } onSelect={onCopyBlockLink}> {t['com.affine.share-menu.copy.block']()} {currentDocMode === 'edgeless' && ( - - } - className={styles.menuItemStyle} - onSelect={onCopyBlockLink} - > + } onSelect={onCopyBlockLink}> {t['com.affine.share-menu.copy.frame']()} )} @@ -347,8 +319,11 @@ export const AFFiNESharePage = (props: ShareMenuProps) => { } >
diff --git a/packages/frontend/core/src/components/blocksuite/block-suite-header/menu/index.tsx b/packages/frontend/core/src/components/blocksuite/block-suite-header/menu/index.tsx index 164b790212..86b1857190 100644 --- a/packages/frontend/core/src/components/blocksuite/block-suite-header/menu/index.tsx +++ b/packages/frontend/core/src/components/blocksuite/block-suite-header/menu/index.tsx @@ -1,7 +1,6 @@ import { toast } from '@affine/component'; import { Menu, - MenuIcon, MenuItem, MenuSeparator, MenuSub, @@ -13,6 +12,7 @@ import { import { PageHistoryModal } from '@affine/core/components/affine/page-history-modal'; import { ShareMenuContent } from '@affine/core/components/affine/share-page-modal/share-menu'; import { Export, MoveToTrash } from '@affine/core/components/page-list'; +import { IsFavoriteIcon } from '@affine/core/components/pure/icons'; import { useBlockSuiteMetaHelper } from '@affine/core/hooks/affine/use-block-suite-meta-helper'; import { useEnableCloud } from '@affine/core/hooks/affine/use-enable-cloud'; import { useExportPage } from '@affine/core/hooks/affine/use-export-page'; @@ -29,8 +29,6 @@ import { DuplicateIcon, EdgelessIcon, EditIcon, - FavoritedIcon, - FavoriteIcon, FrameIcon, HistoryIcon, ImportIcon, @@ -157,10 +155,6 @@ export const PageHeaderMenuButton = ({ : t['com.affine.toastMessage.pageMode']() ); }, [currentMode, editorService, t]); - const menuItemStyle = { - padding: '4px 12px', - transition: 'all 0.3s', - }; const handleMenuOpenChange = useCallback((open: boolean) => { if (open) { @@ -225,11 +219,7 @@ export const PageHeaderMenuButton = ({
} triggerOptions={{ - preFix: ( - - - - ), + prefixIcon: , }} subOptions={{ onOpenChange: handleShareMenuOpenChange, @@ -247,27 +237,17 @@ export const PageHeaderMenuButton = ({ {showResponsiveMenu ? ResponsiveMenuItems : null} {!isJournal && ( - - - } + prefixIcon={} data-testid="editor-option-menu-rename" onSelect={handleRename} - style={menuItemStyle} > {t['Rename']()} )} - {currentMode === 'page' ? : } - - } + prefixIcon={currentMode === 'page' ? : } data-testid="editor-option-menu-edgeless" onSelect={handleSwitchMode} - style={menuItemStyle} > {t['Convert to ']()} {currentMode === 'page' @@ -277,16 +257,7 @@ export const PageHeaderMenuButton = ({ - {favorite ? ( - - ) : ( - - )} - - } + prefixIcon={} > {favorite ? t['com.affine.favoritePageOperation.remove']() @@ -294,28 +265,18 @@ export const PageHeaderMenuButton = ({ - - - } + prefixIcon={} data-testid="editor-option-menu-open-in-new-tab" onSelect={handleOpenInNewTab} - style={menuItemStyle} > {t['com.affine.workbench.tab.page-menu-open']()} {environment.isDesktop && ( - - - } + prefixIcon={} data-testid="editor-option-menu-open-in-split-new" onSelect={handleOpenInSplitView} - style={menuItemStyle} > {t['com.affine.workbench.split-view.page-menu-open']()} @@ -325,81 +286,51 @@ export const PageHeaderMenuButton = ({ {runtimeConfig.enableInfoModal && ( - - - } + prefixIcon={} data-testid="editor-option-menu-info" onSelect={openInfoModal} - style={menuItemStyle} > {t['com.affine.page-properties.page-info.view']()} )} {currentMode === 'page' ? ( - - - } + prefixIcon={} data-testid="editor-option-toc" onSelect={openOutlinePanel} - style={menuItemStyle} > {t['com.affine.header.option.view-toc']()} ) : ( - - - } + prefixIcon={} data-testid="editor-option-frame" onSelect={openAllFrames} - style={menuItemStyle} > {t['com.affine.header.option.view-frame']()} )} - - - } + prefixIcon={} data-testid="editor-option-menu-history" onSelect={openHistoryModal} - style={menuItemStyle} > {t['com.affine.history.view-history-version']()} {!isJournal && ( - - - } + prefixIcon={} data-testid="editor-option-menu-duplicate" onSelect={handleDuplicate} - style={menuItemStyle} > {t['com.affine.header.option.duplicate']()} )} - - - } + prefixIcon={} data-testid="editor-option-menu-import" onSelect={onImportFile} - style={menuItemStyle} > {t['Import']()} diff --git a/packages/frontend/core/src/components/cloud/share-header-right-item/user-avatar.tsx b/packages/frontend/core/src/components/cloud/share-header-right-item/user-avatar.tsx index d6399694cf..a04777f2d6 100644 --- a/packages/frontend/core/src/components/cloud/share-header-right-item/user-avatar.tsx +++ b/packages/frontend/core/src/components/cloud/share-header-right-item/user-avatar.tsx @@ -1,10 +1,5 @@ import { Avatar } from '@affine/component/ui/avatar'; -import { - Menu, - MenuIcon, - MenuItem, - MenuSeparator, -} from '@affine/component/ui/menu'; +import { Menu, MenuItem, MenuSeparator } from '@affine/component/ui/menu'; import { useAsyncCallback } from '@affine/core/hooks/affine-async-hooks'; import { useI18n } from '@affine/i18n'; import { SignOutIcon } from '@blocksuite/icons/rc'; @@ -66,11 +61,7 @@ export const PublishPageUserAvatar = () => { - - - } + prefixIcon={} data-testid="share-page-sign-out-option" onClick={handleSignOut} > diff --git a/packages/frontend/core/src/components/page-list/components/page-display-menu.tsx b/packages/frontend/core/src/components/page-list/components/page-display-menu.tsx index 3e2ed603e8..4086031fec 100644 --- a/packages/frontend/core/src/components/page-list/components/page-display-menu.tsx +++ b/packages/frontend/core/src/components/page-list/components/page-display-menu.tsx @@ -94,7 +94,7 @@ export const PageDisplayMenu = () => { key={option.value} onSelect={() => handleSelect(option.value)} data-active={workspaceProperties.groupBy === option.value} - endFix={ + suffixIcon={ workspaceProperties.groupBy === option.value ? ( ) : null diff --git a/packages/frontend/core/src/components/page-list/filter/vars.tsx b/packages/frontend/core/src/components/page-list/filter/vars.tsx index c1f10d018a..8e534b886d 100644 --- a/packages/frontend/core/src/components/page-list/filter/vars.tsx +++ b/packages/frontend/core/src/components/page-list/filter/vars.tsx @@ -1,4 +1,4 @@ -import { MenuIcon, MenuItem, MenuSeparator } from '@affine/component'; +import { MenuItem, MenuSeparator } from '@affine/component'; import type { Filter, LiteralValue, @@ -92,7 +92,7 @@ export const VariableSelect = ({ // .filter(v => !selected.find(filter => filter.left.name === v.name)) .map(v => ( {variableDefineMap[v.name].icon}} + prefixIcon={variableDefineMap[v.name].icon} key={v.name} onClick={() => { onSelect(createDefaultFilter(v, propertiesMeta)); diff --git a/packages/frontend/core/src/components/page-list/operation-cell.tsx b/packages/frontend/core/src/components/page-list/operation-cell.tsx index 6ebc97f1f7..ff887a6c65 100644 --- a/packages/frontend/core/src/components/page-list/operation-cell.tsx +++ b/packages/frontend/core/src/components/page-list/operation-cell.tsx @@ -1,7 +1,6 @@ import { IconButton, Menu, - MenuIcon, MenuItem, toast, useConfirmModal, @@ -20,8 +19,6 @@ import { DeletePermanentlyIcon, DuplicateIcon, EditIcon, - FavoritedIcon, - FavoriteIcon, FilterIcon, FilterMinusIcon, InformationIcon, @@ -44,6 +41,7 @@ import { useCallback, useState } from 'react'; import type { CollectionService } from '../../modules/collection'; import { InfoModal } from '../affine/page-properties'; import { usePageHelper } from '../blocksuite/block-suite-page-list/utils'; +import { IsFavoriteIcon } from '../pure/icons'; import { FavoriteTag } from './components/favorite-tag'; import * as styles from './list.css'; import { DisablePublicSharing, MoveToTrash } from './operation-menu-items'; @@ -164,76 +162,36 @@ export const PageOperationCell = ({ {isInAllowList && ( - - - } + prefixIcon={} > {t['Remove special filter']()} )} - {favourite ? ( - - ) : ( - - )} - - } + prefixIcon={} > {favourite ? t['com.affine.favoritePageOperation.remove']() : t['com.affine.favoritePageOperation.add']()} {runtimeConfig.enableInfoModal ? ( - - - - } - > + }> {t['com.affine.page-properties.page-info.view']()} ) : null} - - - - } - > + }> {t['com.affine.workbench.tab.page-menu-open']()} {environment.isDesktop && enableSplitView ? ( - - - - } - > + }> {t['com.affine.workbench.split-view.page-menu-open']()} ) : null} - - - - } - onSelect={onDuplicate} - > + } onSelect={onDuplicate}> {t['com.affine.header.option.duplicate']()} @@ -459,17 +417,7 @@ export const CollectionOperationCell = ({ <> - {favourite ? ( - - ) : ( - - )} - - } + prefixIcon={} > {favourite ? t['com.affine.favoritePageOperation.remove']() @@ -477,21 +425,13 @@ export const CollectionOperationCell = ({ - - - } + prefixIcon={} > {t['New Page']()} - - - } + prefixIcon={} type="danger" data-testid="delete-collection" > @@ -566,11 +506,7 @@ export const TagOperationCell = ({ - - - } + prefixIcon={} type="danger" onSelect={handleDelete} data-testid="delete-tag" diff --git a/packages/frontend/core/src/components/page-list/operation-menu-items/disable-public-sharing.tsx b/packages/frontend/core/src/components/page-list/operation-menu-items/disable-public-sharing.tsx index dc1c1808e0..9fc9d7f7af 100644 --- a/packages/frontend/core/src/components/page-list/operation-menu-items/disable-public-sharing.tsx +++ b/packages/frontend/core/src/components/page-list/operation-menu-items/disable-public-sharing.tsx @@ -1,5 +1,5 @@ import type { MenuItemProps } from '@affine/component'; -import { MenuIcon, MenuItem } from '@affine/component'; +import { MenuItem } from '@affine/component'; import { PublicLinkDisableModal } from '@affine/component/disable-public-link'; import { useI18n } from '@affine/i18n'; import { ShareIcon } from '@blocksuite/icons/rc'; @@ -7,15 +7,7 @@ import { ShareIcon } from '@blocksuite/icons/rc'; export const DisablePublicSharing = (props: MenuItemProps) => { const t = useI18n(); return ( - - - - } - {...props} - > + } {...props}> {t['Disable Public Sharing']()} ); diff --git a/packages/frontend/core/src/components/page-list/operation-menu-items/export.tsx b/packages/frontend/core/src/components/page-list/operation-menu-items/export.tsx index de41e8dd2f..893fd95a8c 100644 --- a/packages/frontend/core/src/components/page-list/operation-menu-items/export.tsx +++ b/packages/frontend/core/src/components/page-list/operation-menu-items/export.tsx @@ -1,4 +1,4 @@ -import { MenuIcon, MenuItem, MenuSub } from '@affine/component'; +import { MenuItem, MenuSub } from '@affine/component'; import { track } from '@affine/core/mixpanel'; import { useI18n } from '@affine/i18n'; import { @@ -40,7 +40,7 @@ export function ExportMenuItem({ data-testid={`export-to-${type}`} onSelect={onSelect} block - preFix={{icon}} + prefixIcon={icon} > {label} @@ -127,11 +127,7 @@ export const Export = ({ exportHandler, className, pageMode }: ExportProps) => { items={items} triggerOptions={{ className: transitionStyle, - preFix: ( - - - - ), + prefixIcon: , ['data-testid' as string]: 'export-menu', }} subOptions={{ diff --git a/packages/frontend/core/src/components/page-list/operation-menu-items/move-to-trash.tsx b/packages/frontend/core/src/components/page-list/operation-menu-items/move-to-trash.tsx index fed80a90e1..22ed9cf8d1 100644 --- a/packages/frontend/core/src/components/page-list/operation-menu-items/move-to-trash.tsx +++ b/packages/frontend/core/src/components/page-list/operation-menu-items/move-to-trash.tsx @@ -1,5 +1,5 @@ import type { ConfirmModalProps, MenuItemProps } from '@affine/component'; -import { ConfirmModal, MenuIcon, MenuItem } from '@affine/component'; +import { ConfirmModal, MenuItem } from '@affine/component'; import { useI18n } from '@affine/i18n'; import { DeleteIcon } from '@blocksuite/icons/rc'; @@ -7,15 +7,7 @@ export const MoveToTrash = (props: MenuItemProps) => { const t = useI18n(); return ( - - - - } - type="danger" - {...props} - > + } type="danger" {...props}> {t['com.affine.moveToTrash.title']()} ); diff --git a/packages/frontend/core/src/components/page-list/view/collection-operations.tsx b/packages/frontend/core/src/components/page-list/view/collection-operations.tsx index f53482b3f6..8166642948 100644 --- a/packages/frontend/core/src/components/page-list/view/collection-operations.tsx +++ b/packages/frontend/core/src/components/page-list/view/collection-operations.tsx @@ -1,5 +1,5 @@ import type { MenuItemProps } from '@affine/component'; -import { Menu, MenuIcon, MenuItem } from '@affine/component'; +import { Menu, MenuItem } from '@affine/component'; import { useDeleteCollectionInfo } from '@affine/core/hooks/affine/use-delete-collection-info'; import { CompatibleFavoriteItemsAdapter } from '@affine/core/modules/properties'; import { WorkbenchService } from '@affine/core/modules/workbench'; @@ -8,8 +8,6 @@ import { useI18n } from '@affine/i18n'; import { DeleteIcon, EditIcon, - FavoritedIcon, - FavoriteIcon, FilterIcon, OpenInNewIcon, PlusIcon, @@ -25,6 +23,7 @@ import type { PropsWithChildren, ReactElement } from 'react'; import { useCallback, useMemo } from 'react'; import { CollectionService } from '../../../modules/collection'; +import { IsFavoriteIcon } from '../../pure/icons'; import * as styles from './collection-operations.css'; import { useEditCollection, @@ -127,68 +126,40 @@ export const CollectionOperations = ({ >( () => [ { - icon: ( - - - - ), + icon: , name: t['com.affine.collection.menu.rename'](), click: showEditName, }, { - icon: ( - - - - ), + icon: , name: t['com.affine.collection.menu.edit'](), click: showEdit, }, ...(onAddDocToCollection ? [ { - icon: ( - - - - ), + icon: , name: t['New Page'](), click: onAddDocToCollection, }, ] : []), { - icon: ( - - {favorite ? ( - - ) : ( - - )} - - ), + icon: , name: favorite ? t['com.affine.favoritePageOperation.remove']() : t['com.affine.favoritePageOperation.add'](), click: onToggleFavoritePage, }, { - icon: ( - - - - ), + icon: , name: t['com.affine.workbench.tab.page-menu-open'](), click: openCollectionNewTab, }, ...(enableMultiView && environment.isDesktop ? [ { - icon: ( - - - - ), + icon: , name: t['com.affine.workbench.split-view.page-menu-open'](), click: openCollectionSplitView, }, @@ -198,11 +169,7 @@ export const CollectionOperations = ({ element:
, }, { - icon: ( - - - - ), + icon: , name: t['Delete'](), click: () => { service.deleteCollection(deleteInfo, collection.id); @@ -241,7 +208,7 @@ export const CollectionOperations = ({ data-testid="collection-option" key={action.name} type={action.type} - preFix={action.icon} + prefixIcon={action.icon} onClick={action.click} > {action.name} diff --git a/packages/frontend/core/src/components/pure/icons/favorite-icon.tsx b/packages/frontend/core/src/components/pure/icons/favorite-icon.tsx new file mode 100644 index 0000000000..fbe2265ce2 --- /dev/null +++ b/packages/frontend/core/src/components/pure/icons/favorite-icon.tsx @@ -0,0 +1,18 @@ +import { FavoritedIcon, FavoriteIcon } from '@blocksuite/icons/rc'; +import { cssVar } from '@toeverything/theme'; +import type { SVGProps } from 'react'; + +export const IsFavoriteIcon = ({ + favorite, + style, + ...props +}: { favorite?: boolean } & SVGProps) => { + return favorite ? ( + + ) : ( + + ); +}; diff --git a/packages/frontend/core/src/components/pure/icons/index.tsx b/packages/frontend/core/src/components/pure/icons/index.tsx index a102549c2c..96f96b435e 100644 --- a/packages/frontend/core/src/components/pure/icons/index.tsx +++ b/packages/frontend/core/src/components/pure/icons/index.tsx @@ -1,28 +1 @@ -import { - CloudWorkspaceIcon as DefaultCloudWorkspaceIcon, - CollaborationIcon as DefaultJoinedWorkspaceIcon, - LocalDataIcon as DefaultLocalDataIcon, - LocalWorkspaceIcon as DefaultLocalWorkspaceIcon, - PublishIcon as DefaultPublishIcon, -} from '@blocksuite/icons/rc'; - -// Here are some icons with special color or size - -export const JoinedWorkspaceIcon = () => { - return ; -}; -export const LocalWorkspaceIcon = () => { - return ; -}; - -export const CloudWorkspaceIcon = () => { - return ; -}; - -export const LocalDataIcon = () => { - return ; -}; - -export const PublishIcon = () => { - return ; -}; +export * from './favorite-icon'; diff --git a/packages/frontend/core/src/components/pure/workspace-slider-bar/user-with-workspace-list/add-workspace/index.tsx b/packages/frontend/core/src/components/pure/workspace-slider-bar/user-with-workspace-list/add-workspace/index.tsx index ebfcb5aad8..57d081e7f5 100644 --- a/packages/frontend/core/src/components/pure/workspace-slider-bar/user-with-workspace-list/add-workspace/index.tsx +++ b/packages/frontend/core/src/components/pure/workspace-slider-bar/user-with-workspace-list/add-workspace/index.tsx @@ -18,7 +18,7 @@ export const AddWorkspace = ({ {environment.isDesktop ? ( } + prefixIcon={} onClick={onAddWorkspace} data-testid="add-workspace" className={styles.ItemContainer} @@ -30,7 +30,7 @@ export const AddWorkspace = ({ ) : null} } + prefixIcon={} onClick={onNewWorkspace} data-testid="new-workspace" className={styles.ItemContainer} 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 70410c79e8..5d31238568 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 @@ -4,7 +4,6 @@ import { ErrorMessage, IconButton, Menu, - MenuIcon, MenuItem, type MenuProps, Skeleton, @@ -100,22 +99,14 @@ const AccountMenu = () => { return ( <> - - - } + prefixIcon={} data-testid="workspace-modal-account-settings-option" onClick={onOpenAccountSetting} > {t['com.affine.workspace.cloud.account.settings']()} - - - } + prefixIcon={} data-testid="workspace-modal-sign-out-option" onClick={onOpenSignOutModal} > diff --git a/packages/frontend/core/src/modules/explorer/views/nodes/collection/index.tsx b/packages/frontend/core/src/modules/explorer/views/nodes/collection/index.tsx index 319c09f211..7ee474cf94 100644 --- a/packages/frontend/core/src/modules/explorer/views/nodes/collection/index.tsx +++ b/packages/frontend/core/src/modules/explorer/views/nodes/collection/index.tsx @@ -2,7 +2,6 @@ import { AnimatedCollectionsIcon, type DropTargetDropEvent, type DropTargetOptions, - MenuIcon, MenuItem, toast, } from '@affine/component'; @@ -320,11 +319,7 @@ const ExplorerCollectionNodeChildren = ({ index: 99, view: ( - - - } + prefixIcon={} onClick={() => handleRemoveFromAllowList(doc.id)} > {t['Remove special filter']()} 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 e26bdf0be2..c92c6d97c9 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 @@ -1,10 +1,10 @@ import { IconButton, - MenuIcon, MenuItem, MenuSeparator, useConfirmModal, } from '@affine/component'; +import { IsFavoriteIcon } from '@affine/core/components/pure/icons'; import { useDeleteCollectionInfo } from '@affine/core/hooks/affine/use-delete-collection-info'; import { track } from '@affine/core/mixpanel'; import { CollectionService } from '@affine/core/modules/collection'; @@ -13,8 +13,6 @@ import { WorkbenchService } from '@affine/core/modules/workbench'; import { useI18n } from '@affine/i18n'; import { DeleteIcon, - FavoritedIcon, - FavoriteIcon, FilterIcon, OpenInNewIcon, PlusIcon, @@ -143,14 +141,7 @@ export const useExplorerCollectionNodeOperations = ( { index: 99, view: ( - - - - } - onClick={handleShowEdit} - > + } onClick={handleShowEdit}> {t['com.affine.collection.menu.edit']()} ), @@ -159,11 +150,7 @@ export const useExplorerCollectionNodeOperations = ( index: 99, view: ( - - - } + prefixIcon={} onClick={handleAddDocToCollection} > {t['New Page']()} @@ -174,17 +161,7 @@ export const useExplorerCollectionNodeOperations = ( index: 99, view: ( - {favorite ? ( - - ) : ( - - )} - - } + prefixIcon={} onClick={handleToggleFavoriteCollection} > {favorite @@ -196,14 +173,7 @@ export const useExplorerCollectionNodeOperations = ( { index: 99, view: ( - - - - } - onClick={handleOpenInNewTab} - > + } onClick={handleOpenInNewTab}> {t['com.affine.workbench.tab.page-menu-open']()} ), @@ -214,11 +184,7 @@ export const useExplorerCollectionNodeOperations = ( index: 99, view: ( - - - } + prefixIcon={} onClick={handleOpenInSplitView} > {t['com.affine.workbench.split-view.page-menu-open']()} @@ -236,11 +202,7 @@ export const useExplorerCollectionNodeOperations = ( view: ( - - - } + prefixIcon={} onClick={handleDeleteCollection} > {t['Delete']()} 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 8023529ab6..fc88913dd0 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 @@ -1,11 +1,11 @@ import { IconButton, - MenuIcon, MenuItem, MenuSeparator, toast, useConfirmModal, } from '@affine/component'; +import { IsFavoriteIcon } from '@affine/core/components/pure/icons'; import { useAsyncCallback } from '@affine/core/hooks/affine-async-hooks'; import { track } from '@affine/core/mixpanel'; import { CompatibleFavoriteItemsAdapter } from '@affine/core/modules/properties'; @@ -13,8 +13,6 @@ import { WorkbenchService } from '@affine/core/modules/workbench'; import { useI18n } from '@affine/i18n'; import { DeleteIcon, - FavoritedIcon, - FavoriteIcon, InformationIcon, LinkedPageIcon, OpenInNewIcon, @@ -147,11 +145,7 @@ export const useExplorerDocNodeOperations = ( index: 50, view: ( - - - } + prefixIcon={} onClick={handleOpenInfoModal} > {t['com.affine.page-properties.page-info.view']()} @@ -164,11 +158,7 @@ export const useExplorerDocNodeOperations = ( index: 99, view: ( - - - } + prefixIcon={} onClick={handleAddLinkedPage} > {t['com.affine.page-operation.add-linked-page']()} @@ -178,14 +168,7 @@ export const useExplorerDocNodeOperations = ( { index: 99, view: ( - - - - } - onClick={handleOpenInNewTab} - > + } onClick={handleOpenInNewTab}> {t['com.affine.workbench.tab.page-menu-open']()} ), @@ -196,11 +179,7 @@ export const useExplorerDocNodeOperations = ( index: 100, view: ( - - - } + prefixIcon={} onClick={handleOpenInSplitView} > {t['com.affine.workbench.split-view.page-menu-open']()} @@ -213,17 +192,7 @@ export const useExplorerDocNodeOperations = ( index: 199, view: ( - {favorite ? ( - - ) : ( - - )} - - } + prefixIcon={} onClick={handleToggleFavoriteDoc} > {favorite @@ -241,11 +210,7 @@ export const useExplorerDocNodeOperations = ( view: ( - - - } + prefixIcon={} onClick={handleMoveToTrash} > {t['com.affine.moveToTrash.title']()} 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 49ff93521c..eeeaa04696 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 @@ -4,7 +4,6 @@ import { type DropTargetDropEvent, type DropTargetOptions, IconButton, - MenuIcon, MenuItem, MenuSeparator, MenuSub, @@ -632,14 +631,7 @@ export const ExplorerFolderNodeFolder = ({ { index: 100, view: ( - - - - } - onClick={handleCreateSubfolder} - > + } onClick={handleCreateSubfolder}> {t['com.affine.rootAppSidebar.organize.folder.create-subfolder']()} ), @@ -648,11 +640,7 @@ export const ExplorerFolderNodeFolder = ({ index: 101, view: ( - - - } + prefixIcon={} onClick={() => handleAddToFolder('doc')} > {t['com.affine.rootAppSidebar.organize.folder.add-docs']()} @@ -664,31 +652,19 @@ export const ExplorerFolderNodeFolder = ({ view: ( - - - ), + prefixIcon: , }} items={ <> handleAddToFolder('tag')} - preFix={ - - - - } + prefixIcon={} > {t['com.affine.rootAppSidebar.organize.folder.add-tags']()} handleAddToFolder('collection')} - preFix={ - - - - } + prefixIcon={} > {t[ 'com.affine.rootAppSidebar.organize.folder.add-collections' @@ -716,11 +692,7 @@ export const ExplorerFolderNodeFolder = ({ view: ( - - - } + prefixIcon={} onClick={handleDelete} > {t['com.affine.rootAppSidebar.organize.delete']()} @@ -754,11 +726,7 @@ export const ExplorerFolderNodeFolder = ({ view: ( - - - } + prefixIcon={} data-event-props="$.navigationPanel.organize.deleteOrganizeItem" data-event-args-type={node.type$.value} onClick={() => node.delete()} diff --git a/packages/frontend/core/src/modules/explorer/views/nodes/folder/operations.tsx b/packages/frontend/core/src/modules/explorer/views/nodes/folder/operations.tsx index 14ffdd1640..f57156755f 100644 --- a/packages/frontend/core/src/modules/explorer/views/nodes/folder/operations.tsx +++ b/packages/frontend/core/src/modules/explorer/views/nodes/folder/operations.tsx @@ -1,9 +1,8 @@ -import { MenuIcon, MenuItem } from '@affine/component'; +import { MenuItem } from '@affine/component'; +import { IsFavoriteIcon } from '@affine/core/components/pure/icons'; import { CompatibleFavoriteItemsAdapter } from '@affine/core/modules/properties'; import { useI18n } from '@affine/i18n'; -import { FavoritedIcon, FavoriteIcon } from '@blocksuite/icons/rc'; import { useLiveData, useService } from '@toeverything/infra'; -import { cssVar } from '@toeverything/theme'; import { useMemo } from 'react'; export const FavoriteFolderOperation = ({ id }: { id: string }) => { @@ -20,15 +19,7 @@ export const FavoriteFolderOperation = ({ id }: { id: string }) => { return ( - {favorite ? ( - - ) : ( - - )} - - } + prefixIcon={} onClick={() => compatibleFavoriteItemsAdapter.toggle(id, 'folder')} > {favorite 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 9e4a941523..63ec757713 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 @@ -1,10 +1,5 @@ -import { - IconButton, - MenuIcon, - MenuItem, - MenuSeparator, - toast, -} from '@affine/component'; +import { IconButton, MenuItem, MenuSeparator, toast } from '@affine/component'; +import { IsFavoriteIcon } from '@affine/core/components/pure/icons'; import { track } from '@affine/core/mixpanel'; import { FavoriteService } from '@affine/core/modules/favorite'; import { TagService } from '@affine/core/modules/tag'; @@ -12,8 +7,6 @@ import { WorkbenchService } from '@affine/core/modules/workbench'; import { useI18n } from '@affine/i18n'; import { DeleteIcon, - FavoritedIcon, - FavoriteIcon, OpenInNewIcon, PlusIcon, SplitViewIcon, @@ -114,14 +107,7 @@ export const useExplorerTagNodeOperations = ( { index: 50, view: ( - - - - } - onClick={handleOpenInNewTab} - > + } onClick={handleOpenInNewTab}> {t['com.affine.workbench.tab.page-menu-open']()} ), @@ -132,11 +118,7 @@ export const useExplorerTagNodeOperations = ( index: 100, view: ( - - - } + prefixIcon={} onClick={handleOpenInSplitView} > {t['com.affine.workbench.split-view.page-menu-open']()} @@ -149,17 +131,7 @@ export const useExplorerTagNodeOperations = ( index: 199, view: ( - {favorite ? ( - - ) : ( - - )} - - } + prefixIcon={} onClick={handleToggleFavoriteTag} > {favorite @@ -177,11 +149,7 @@ export const useExplorerTagNodeOperations = ( view: ( - - - } + prefixIcon={} onClick={handleMoveToTrash} > {t['Delete']()} 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 9b9551ab15..de2cdeca2a 100644 --- a/packages/frontend/core/src/modules/explorer/views/tree/node.tsx +++ b/packages/frontend/core/src/modules/explorer/views/tree/node.tsx @@ -5,7 +5,6 @@ import { type DropTargetTreeInstruction, IconButton, Menu, - MenuIcon, MenuItem, useDraggable, useDropTarget, @@ -246,11 +245,7 @@ export const ExplorerTreeNode = ({ - - - } + prefixIcon={} onClick={() => setRenaming(true)} > {t['com.affine.menu.rename']()} diff --git a/packages/frontend/core/src/modules/workbench/view/split-view/panel.tsx b/packages/frontend/core/src/modules/workbench/view/split-view/panel.tsx index 7448073165..3760686108 100644 --- a/packages/frontend/core/src/modules/workbench/view/split-view/panel.tsx +++ b/packages/frontend/core/src/modules/workbench/view/split-view/panel.tsx @@ -1,4 +1,4 @@ -import { MenuIcon, MenuItem } from '@affine/component'; +import { MenuItem } from '@affine/component'; import { useI18n } from '@affine/i18n'; import { ExpandCloseIcon, @@ -137,30 +137,21 @@ const SplitViewMenu = ({ view }: { view: View }) => { const CloseItem = views.length > 1 ? ( - } />} - onClick={handleClose} - > + } onClick={handleClose}> {t['com.affine.workbench.split-view-menu.close']()} ) : null; const MoveLeftItem = viewIndex > 0 && views.length > 1 ? ( - } />} - > + }> {t['com.affine.workbench.split-view-menu.move-left']()} ) : null; const FullScreenItem = views.length > 1 ? ( - } />} - > + }> {t['com.affine.workbench.split-view-menu.keep-this-one']()} ) : null; @@ -169,7 +160,7 @@ const SplitViewMenu = ({ view }: { view: View }) => { viewIndex < views.length - 1 ? ( } />} + prefixIcon={} > {t['com.affine.workbench.split-view-menu.move-right']()} diff --git a/packages/frontend/graphql/src/schema.ts b/packages/frontend/graphql/src/schema.ts index ae8c81cae6..2ba9e554bc 100644 --- a/packages/frontend/graphql/src/schema.ts +++ b/packages/frontend/graphql/src/schema.ts @@ -38,10 +38,15 @@ export interface Scalars { Upload: { input: File; output: File }; } +export interface AlreadyInSpaceDataType { + __typename?: 'AlreadyInSpaceDataType'; + spaceId: Scalars['String']['output']; +} + export interface BlobNotFoundDataType { __typename?: 'BlobNotFoundDataType'; blobId: Scalars['String']['output']; - workspaceId: Scalars['String']['output']; + spaceId: Scalars['String']['output']; } export enum ChatHistoryOrder { @@ -221,14 +226,14 @@ export interface DeleteSessionInput { export interface DocAccessDeniedDataType { __typename?: 'DocAccessDeniedDataType'; docId: Scalars['String']['output']; - workspaceId: Scalars['String']['output']; + spaceId: Scalars['String']['output']; } export interface DocHistoryNotFoundDataType { __typename?: 'DocHistoryNotFoundDataType'; docId: Scalars['String']['output']; + spaceId: Scalars['String']['output']; timestamp: Scalars['Int']['output']; - workspaceId: Scalars['String']['output']; } export interface DocHistoryType { @@ -241,10 +246,11 @@ export interface DocHistoryType { export interface DocNotFoundDataType { __typename?: 'DocNotFoundDataType'; docId: Scalars['String']['output']; - workspaceId: Scalars['String']['output']; + spaceId: Scalars['String']['output']; } export type ErrorDataUnion = + | AlreadyInSpaceDataType | BlobNotFoundDataType | CopilotMessageNotFoundDataType | CopilotPromptNotFoundDataType @@ -256,27 +262,28 @@ export type ErrorDataUnion = | InvalidPasswordLengthDataType | InvalidRuntimeConfigTypeDataType | MissingOauthQueryParameterDataType - | NotInWorkspaceDataType + | NotInSpaceDataType | RuntimeConfigNotFoundDataType | SameSubscriptionRecurringDataType + | SpaceAccessDeniedDataType + | SpaceNotFoundDataType + | SpaceOwnerNotFoundDataType | SubscriptionAlreadyExistsDataType | SubscriptionNotExistsDataType | SubscriptionPlanNotFoundDataType | UnknownOauthProviderDataType - | VersionRejectedDataType - | WorkspaceAccessDeniedDataType - | WorkspaceNotFoundDataType - | WorkspaceOwnerNotFoundDataType; + | VersionRejectedDataType; export enum ErrorNames { ACCESS_DENIED = 'ACCESS_DENIED', ACTION_FORBIDDEN = 'ACTION_FORBIDDEN', + ALREADY_IN_SPACE = 'ALREADY_IN_SPACE', AUTHENTICATION_REQUIRED = 'AUTHENTICATION_REQUIRED', BLOB_NOT_FOUND = 'BLOB_NOT_FOUND', BLOB_QUOTA_EXCEEDED = 'BLOB_QUOTA_EXCEEDED', CANNOT_DELETE_ALL_ADMIN_ACCOUNT = 'CANNOT_DELETE_ALL_ADMIN_ACCOUNT', CANNOT_DELETE_OWN_ACCOUNT = 'CANNOT_DELETE_OWN_ACCOUNT', - CANT_CHANGE_WORKSPACE_OWNER = 'CANT_CHANGE_WORKSPACE_OWNER', + CANT_CHANGE_SPACE_OWNER = 'CANT_CHANGE_SPACE_OWNER', CANT_UPDATE_LIFETIME_SUBSCRIPTION = 'CANT_UPDATE_LIFETIME_SUBSCRIPTION', COPILOT_ACTION_TAKEN = 'COPILOT_ACTION_TAKEN', COPILOT_FAILED_TO_CREATE_MESSAGE = 'COPILOT_FAILED_TO_CREATE_MESSAGE', @@ -299,6 +306,8 @@ export enum ErrorNames { EXPECT_TO_PUBLISH_PAGE = 'EXPECT_TO_PUBLISH_PAGE', EXPECT_TO_REVOKE_PUBLIC_PAGE = 'EXPECT_TO_REVOKE_PUBLIC_PAGE', FAILED_TO_CHECKOUT = 'FAILED_TO_CHECKOUT', + FAILED_TO_SAVE_UPDATES = 'FAILED_TO_SAVE_UPDATES', + FAILED_TO_UPSERT_SNAPSHOT = 'FAILED_TO_UPSERT_SNAPSHOT', INTERNAL_SERVER_ERROR = 'INTERNAL_SERVER_ERROR', INVALID_EMAIL = 'INVALID_EMAIL', INVALID_EMAIL_TOKEN = 'INVALID_EMAIL_TOKEN', @@ -311,7 +320,7 @@ export enum ErrorNames { MEMBER_QUOTA_EXCEEDED = 'MEMBER_QUOTA_EXCEEDED', MISSING_OAUTH_QUERY_PARAMETER = 'MISSING_OAUTH_QUERY_PARAMETER', NOT_FOUND = 'NOT_FOUND', - NOT_IN_WORKSPACE = 'NOT_IN_WORKSPACE', + NOT_IN_SPACE = 'NOT_IN_SPACE', NO_COPILOT_PROVIDER_AVAILABLE = 'NO_COPILOT_PROVIDER_AVAILABLE', OAUTH_ACCOUNT_ALREADY_CONNECTED = 'OAUTH_ACCOUNT_ALREADY_CONNECTED', OAUTH_STATE_EXPIRED = 'OAUTH_STATE_EXPIRED', @@ -321,6 +330,9 @@ export enum ErrorNames { SAME_EMAIL_PROVIDED = 'SAME_EMAIL_PROVIDED', SAME_SUBSCRIPTION_RECURRING = 'SAME_SUBSCRIPTION_RECURRING', SIGN_UP_FORBIDDEN = 'SIGN_UP_FORBIDDEN', + SPACE_ACCESS_DENIED = 'SPACE_ACCESS_DENIED', + SPACE_NOT_FOUND = 'SPACE_NOT_FOUND', + SPACE_OWNER_NOT_FOUND = 'SPACE_OWNER_NOT_FOUND', SUBSCRIPTION_ALREADY_EXISTS = 'SUBSCRIPTION_ALREADY_EXISTS', SUBSCRIPTION_EXPIRED = 'SUBSCRIPTION_EXPIRED', SUBSCRIPTION_HAS_BEEN_CANCELED = 'SUBSCRIPTION_HAS_BEEN_CANCELED', @@ -332,9 +344,6 @@ export enum ErrorNames { USER_AVATAR_NOT_FOUND = 'USER_AVATAR_NOT_FOUND', USER_NOT_FOUND = 'USER_NOT_FOUND', VERSION_REJECTED = 'VERSION_REJECTED', - WORKSPACE_ACCESS_DENIED = 'WORKSPACE_ACCESS_DENIED', - WORKSPACE_NOT_FOUND = 'WORKSPACE_NOT_FOUND', - WORKSPACE_OWNER_NOT_FOUND = 'WORKSPACE_OWNER_NOT_FOUND', WRONG_SIGN_IN_CREDENTIALS = 'WRONG_SIGN_IN_CREDENTIALS', WRONG_SIGN_IN_METHOD = 'WRONG_SIGN_IN_METHOD', } @@ -749,9 +758,9 @@ export interface MutationVerifyEmailArgs { token: Scalars['String']['input']; } -export interface NotInWorkspaceDataType { - __typename?: 'NotInWorkspaceDataType'; - workspaceId: Scalars['String']['output']; +export interface NotInSpaceDataType { + __typename?: 'NotInSpaceDataType'; + spaceId: Scalars['String']['output']; } export enum OAuthProviderType { @@ -977,6 +986,21 @@ export interface ServerServiceConfig { name: Scalars['String']['output']; } +export interface SpaceAccessDeniedDataType { + __typename?: 'SpaceAccessDeniedDataType'; + spaceId: Scalars['String']['output']; +} + +export interface SpaceNotFoundDataType { + __typename?: 'SpaceNotFoundDataType'; + spaceId: Scalars['String']['output']; +} + +export interface SpaceOwnerNotFoundDataType { + __typename?: 'SpaceOwnerNotFoundDataType'; + spaceId: Scalars['String']['output']; +} + export interface SubscriptionAlreadyExistsDataType { __typename?: 'SubscriptionAlreadyExistsDataType'; plan: Scalars['String']['output']; @@ -1152,26 +1176,11 @@ export interface VersionRejectedDataType { version: Scalars['String']['output']; } -export interface WorkspaceAccessDeniedDataType { - __typename?: 'WorkspaceAccessDeniedDataType'; - workspaceId: Scalars['String']['output']; -} - export interface WorkspaceBlobSizes { __typename?: 'WorkspaceBlobSizes'; size: Scalars['SafeInt']['output']; } -export interface WorkspaceNotFoundDataType { - __typename?: 'WorkspaceNotFoundDataType'; - workspaceId: Scalars['String']['output']; -} - -export interface WorkspaceOwnerNotFoundDataType { - __typename?: 'WorkspaceOwnerNotFoundDataType'; - workspaceId: Scalars['String']['output']; -} - export interface WorkspacePage { __typename?: 'WorkspacePage'; id: Scalars['String']['output']; diff --git a/yarn.lock b/yarn.lock index bcd22d1fd7..c4bc25eca9 100644 --- a/yarn.lock +++ b/yarn.lock @@ -310,6 +310,7 @@ __metadata: "@radix-ui/react-popover": "npm:^1.0.7" "@radix-ui/react-radio-group": "npm:^1.1.3" "@radix-ui/react-scroll-area": "npm:^1.0.5" + "@radix-ui/react-slot": "npm:^1.1.0" "@radix-ui/react-tabs": "npm:^1.1.0" "@radix-ui/react-toast": "npm:^1.1.5" "@radix-ui/react-toolbar": "npm:^1.0.4" @@ -325,7 +326,7 @@ __metadata: "@storybook/react": "npm:^8.2.9" "@storybook/react-vite": "npm:^8.2.9" "@testing-library/react": "npm:^16.0.0" - "@toeverything/theme": "npm:^1.0.4" + "@toeverything/theme": "npm:^1.0.5" "@types/bytes": "npm:^3.1.4" "@types/react": "npm:^18.2.75" "@types/react-dnd": "npm:^3.0.2" @@ -421,7 +422,7 @@ __metadata: "@sgtpooki/file-type": "npm:^1.0.1" "@swc/core": "npm:^1.4.13" "@testing-library/react": "npm:^16.0.0" - "@toeverything/theme": "npm:^1.0.4" + "@toeverything/theme": "npm:^1.0.5" "@types/animejs": "npm:^3.1.12" "@types/bytes": "npm:^3.1.4" "@types/image-blob-reduce": "npm:^4.1.4" @@ -13857,7 +13858,7 @@ __metadata: languageName: unknown linkType: soft -"@toeverything/theme@npm:^1.0.2, @toeverything/theme@npm:^1.0.4": +"@toeverything/theme@npm:^1.0.2, @toeverything/theme@npm:^1.0.5": version: 1.0.5 resolution: "@toeverything/theme@npm:1.0.5" checksum: 10/26f177192c546b8b6c953a4d75da5520486fa92c872b889052861cd121ef9840b5006e2f1513988117290066812e08015aa99b025d13c01ca50563a9ba26a732 @@ -14624,12 +14625,12 @@ __metadata: languageName: node linkType: hard -"@types/node@npm:*, @types/node@npm:>=10.0.0, @types/node@npm:>=13.7.0, @types/node@npm:>=8.1.0": - version: 22.4.1 - resolution: "@types/node@npm:22.4.1" +"@types/node@npm:*, @types/node@npm:>=10.0.0, @types/node@npm:>=13.7.0, @types/node@npm:>=8.1.0, @types/node@npm:^20.12.7, @types/node@npm:^20.14.9, @types/node@npm:^20.9.0": + version: 20.16.1 + resolution: "@types/node@npm:20.16.1" dependencies: undici-types: "npm:~6.19.2" - checksum: 10/cce9221aea24688bccc3d7c25afd26d95c1bdab73a32ee5c43bb456e070116abf6ba537a9ff19c728bc06fe054e69a2c7ec8e5d818e34a3bf9567eae2cb20059 + checksum: 10/9bae1dffd2094694147a91ebec51dc89a60a607d16d47a0d770320f1a75d3ba58663708fd93c37954a63acb701a4e0fd64245139c57ae810d3ad524e75481d4e languageName: node linkType: hard @@ -14642,15 +14643,6 @@ __metadata: languageName: node linkType: hard -"@types/node@npm:^20.12.7, @types/node@npm:^20.14.9, @types/node@npm:^20.9.0": - version: 20.16.1 - resolution: "@types/node@npm:20.16.1" - dependencies: - undici-types: "npm:~6.19.2" - checksum: 10/9bae1dffd2094694147a91ebec51dc89a60a607d16d47a0d770320f1a75d3ba58663708fd93c37954a63acb701a4e0fd64245139c57ae810d3ad524e75481d4e - languageName: node - linkType: hard - "@types/nodemailer@npm:^6.4.14": version: 6.4.15 resolution: "@types/nodemailer@npm:6.4.15"