feat: replace menu with new design (#4012)

Co-authored-by: Alex Yang <himself65@outlook.com>
This commit is contained in:
Qi 2023-09-06 12:36:43 +08:00 committed by GitHub
parent ef3d3a34e2
commit d8c9f10bc1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
51 changed files with 611 additions and 739 deletions

View File

@ -28,7 +28,7 @@
"@blocksuite/blocks": "0.0.0-20230829150056-df43987c-nightly", "@blocksuite/blocks": "0.0.0-20230829150056-df43987c-nightly",
"@blocksuite/editor": "0.0.0-20230829150056-df43987c-nightly", "@blocksuite/editor": "0.0.0-20230829150056-df43987c-nightly",
"@blocksuite/global": "0.0.0-20230829150056-df43987c-nightly", "@blocksuite/global": "0.0.0-20230829150056-df43987c-nightly",
"@blocksuite/icons": "^2.1.31", "@blocksuite/icons": "^2.1.32",
"@blocksuite/lit": "0.0.0-20230829150056-df43987c-nightly", "@blocksuite/lit": "0.0.0-20230829150056-df43987c-nightly",
"@blocksuite/store": "0.0.0-20230829150056-df43987c-nightly", "@blocksuite/store": "0.0.0-20230829150056-df43987c-nightly",
"@dnd-kit/core": "^6.0.8", "@dnd-kit/core": "^6.0.8",
@ -40,7 +40,7 @@
"@mui/material": "^5.14.7", "@mui/material": "^5.14.7",
"@radix-ui/react-select": "^1.2.2", "@radix-ui/react-select": "^1.2.2",
"@react-hookz/web": "^23.1.0", "@react-hookz/web": "^23.1.0",
"@toeverything/components": "^0.0.25", "@toeverything/components": "^0.0.31",
"async-call-rpc": "^6.3.1", "async-call-rpc": "^6.3.1",
"cmdk": "^0.2.0", "cmdk": "^0.2.0",
"css-spring": "^4.1.0", "css-spring": "^4.1.0",

View File

@ -1,138 +1,69 @@
import { import { LOCALES } from '@affine/i18n';
Menu, import { useI18N } from '@affine/i18n';
MenuItem, import { Menu, MenuItem, MenuTrigger } from '@toeverything/components/menu';
type MenuProps, import type { ReactElement } from 'react';
MenuTrigger, import { useCallback, useMemo, useRef } from 'react';
styled,
} from '@affine/component';
import { LOCALES, useI18N } from '@affine/i18n';
import { assertExists } from '@blocksuite/global/utils';
import type { ButtonProps } from '@toeverything/components/button';
import { useCallback, useEffect, useState } from 'react';
export const StyledListItem = styled(MenuItem)(() => ({
height: '38px',
textTransform: 'capitalize',
}));
interface LanguageMenuContentProps {
currentLanguage: string;
currentLanguageIndex: number;
}
// Fixme: keyboard focus should be supported by Menu component
const LanguageMenuContent = ({ const LanguageMenuContent = ({
currentLanguage, currentLanguage,
currentLanguageIndex, onSelect,
}: LanguageMenuContentProps) => { }: {
const i18n = useI18N(); currentLanguage?: string;
const changeLanguage = useCallback( onSelect: (value: string) => void;
(targetLanguage: string) => { }) => {
console.assert(
LOCALES.some(item => item.tag === targetLanguage),
'targetLanguage should be one of the LOCALES'
);
i18n.changeLanguage(targetLanguage).catch(err => {
console.error('Failed to change language', err);
});
},
[i18n]
);
const [focusedOptionIndex, setFocusedOptionIndex] = useState(
currentLanguageIndex ?? 0
);
const handleKeyDown = useCallback(
(event: KeyboardEvent) => {
switch (event.key) {
case 'ArrowUp':
event.preventDefault();
setFocusedOptionIndex(prevIndex =>
prevIndex > 0 ? prevIndex - 1 : 0
);
break;
case 'ArrowDown':
event.preventDefault();
setFocusedOptionIndex(prevIndex =>
prevIndex < LOCALES.length - 1 ? prevIndex + 1 : LOCALES.length
);
break;
case 'Enter':
if (focusedOptionIndex !== -1) {
const selectedOption = LOCALES[focusedOptionIndex];
changeLanguage(selectedOption.tag);
}
break;
default:
break;
}
},
[changeLanguage, focusedOptionIndex]
);
useEffect(() => {
document.addEventListener('keydown', handleKeyDown);
return () => {
document.removeEventListener('keydown', handleKeyDown);
};
}, [handleKeyDown]);
return ( return (
<> <>
{LOCALES.map((option, optionIndex) => { {LOCALES.map(option => {
return ( return (
<StyledListItem <MenuItem
key={option.name} key={option.name}
active={option.tag === currentLanguage} selected={currentLanguage === option.originalName}
userFocused={optionIndex == focusedOptionIndex}
title={option.name} title={option.name}
onClick={() => { onSelect={() => onSelect(option.tag)}
changeLanguage(option.tag);
}}
> >
{option.originalName} {option.originalName}
</StyledListItem> </MenuItem>
); );
})} })}
</> </>
); );
}; };
interface LanguageMenuProps extends Omit<MenuProps, 'children'> { export const LanguageMenu = () => {
triggerProps?: ButtonProps;
}
export const LanguageMenu = ({
triggerProps,
...menuProps
}: LanguageMenuProps) => {
const i18n = useI18N(); const i18n = useI18N();
const ref = useRef(null);
const currentLanguageIndex = LOCALES.findIndex( const currentLanguage = useMemo(
item => item.tag === i18n.language () => LOCALES.find(item => item.tag === i18n.language),
[i18n.language]
);
const onSelect = useCallback(
(event: string) => {
return i18n.changeLanguage(event);
},
[i18n]
); );
const currentLanguage = LOCALES[currentLanguageIndex];
assertExists(currentLanguage, 'currentLanguage should exist');
return ( return (
<Menu <Menu
content={ items={
<LanguageMenuContent (
currentLanguage={currentLanguage.tag} <LanguageMenuContent
currentLanguageIndex={currentLanguageIndex} currentLanguage={currentLanguage?.originalName}
/> onSelect={onSelect}
/>
) as ReactElement
} }
placement="bottom-end" portalOptions={{
trigger="click" container: ref.current,
disablePortal={true} }}
{...menuProps}
> >
<MenuTrigger <MenuTrigger
ref={ref}
data-testid="language-menu-button" data-testid="language-menu-button"
style={{ textTransform: 'capitalize' }} style={{ textTransform: 'capitalize', fontWeight: 600 }}
{...triggerProps} block={true}
> >
{currentLanguage.originalName} {currentLanguage?.originalName || ''}
</MenuTrigger> </MenuTrigger>
</Menu> </Menu>
); );

View File

@ -0,0 +1,5 @@
import { style } from '@vanilla-extract/css';
export const hoveredLanguageItem = style({
background: 'var(--affine-hover-color)',
});

View File

@ -1,4 +1,3 @@
import { Menu, MenuItem } from '@affine/component';
import { import {
InviteModal, InviteModal,
type InviteModalProps, type InviteModalProps,
@ -12,6 +11,7 @@ import { useAFFiNEI18N } from '@affine/i18n/hooks';
import { MoreVerticalIcon } from '@blocksuite/icons'; import { MoreVerticalIcon } from '@blocksuite/icons';
import { Avatar } from '@toeverything/components/avatar'; import { Avatar } from '@toeverything/components/avatar';
import { Button, IconButton } from '@toeverything/components/button'; import { Button, IconButton } from '@toeverything/components/button';
import { Menu, MenuItem } from '@toeverything/components/menu';
import { Tooltip } from '@toeverything/components/tooltip'; import { Tooltip } from '@toeverything/components/tooltip';
import clsx from 'clsx'; import clsx from 'clsx';
import { useSetAtom } from 'jotai/react'; import { useSetAtom } from 'jotai/react';
@ -190,14 +190,11 @@ const MemberItem = ({
: 'Pending'} : 'Pending'}
</div> </div>
<Menu <Menu
content={ items={
<MenuItem data-member-id={member.id} onClick={handleRevoke}> <MenuItem data-member-id={member.id} onClick={handleRevoke}>
{operationButtonInfo.leaveOrRevokeText} {operationButtonInfo.leaveOrRevokeText}
</MenuItem> </MenuItem>
} }
placement="bottom"
disablePortal={true}
trigger="click"
> >
<IconButton <IconButton
disabled={!operationButtonInfo.show} disabled={!operationButtonInfo.show}

View File

@ -1,6 +1,6 @@
import { Menu, MenuItem, MenuTrigger } from '@affine/component'; import { Menu, MenuItem, MenuTrigger } from '@toeverything/components/menu';
import dayjs from 'dayjs'; import dayjs from 'dayjs';
import { useCallback } from 'react'; import { useCallback, useRef } from 'react';
import { import {
dateFormatOptions, dateFormatOptions,
@ -23,10 +23,8 @@ const DateFormatMenuContent = ({
return ( return (
<MenuItem <MenuItem
key={option} key={option}
active={currentOption === option} selected={currentOption === option}
onClick={() => { onSelect={() => onSelect(option)}
onSelect(option);
}}
> >
{dayjs(new Date()).format(option)} {dayjs(new Date()).format(option)}
</MenuItem> </MenuItem>
@ -37,6 +35,7 @@ const DateFormatMenuContent = ({
}; };
export const DateFormatSetting = () => { export const DateFormatSetting = () => {
const ref = useRef(null);
const [appearanceSettings, setAppSettings] = useAppSetting(); const [appearanceSettings, setAppSettings] = useAppSetting();
const handleSelect = useCallback( const handleSelect = useCallback(
(option: DateFormats) => { (option: DateFormats) => {
@ -47,17 +46,15 @@ export const DateFormatSetting = () => {
return ( return (
<Menu <Menu
content={ items={
<DateFormatMenuContent <DateFormatMenuContent
onSelect={handleSelect} onSelect={handleSelect}
currentOption={appearanceSettings.dateFormat} currentOption={appearanceSettings.dateFormat}
/> />
} }
placement="bottom-end" portalOptions={{ container: ref.current }}
trigger="click"
disablePortal={true}
> >
<MenuTrigger data-testid="date-format-menu-trigger"> <MenuTrigger ref={ref} data-testid="date-format-menu-trigger" block>
{dayjs(new Date()).format(appearanceSettings.dateFormat)} {dayjs(new Date()).format(appearanceSettings.dateFormat)}
</MenuTrigger> </MenuTrigger>
</Menu> </Menu>

View File

@ -113,17 +113,7 @@ export const AppearanceSettings = () => {
desc={t['com.affine.settings.appearance.language-description']()} desc={t['com.affine.settings.appearance.language-description']()}
> >
<div className={settingWrapper}> <div className={settingWrapper}>
<LanguageMenu <LanguageMenu />
triggerContainerStyle={{ width: '100%' }}
triggerProps={{
style: {
width: '100%',
justifyContent: 'space-between',
fontWeight: 600,
padding: '0 10px',
},
}}
/>
</div> </div>
</SettingRow> </SettingRow>
{environment.isDesktop ? ( {environment.isDesktop ? (

View File

@ -1,4 +1,4 @@
import { FlexWrapper, Menu, MenuItem } from '@affine/component'; import { FlexWrapper } from '@affine/component';
import { Export, MoveToTrash } from '@affine/component/page-list'; import { Export, MoveToTrash } from '@affine/component/page-list';
import { useAFFiNEI18N } from '@affine/i18n/hooks'; import { useAFFiNEI18N } from '@affine/i18n/hooks';
import { assertExists } from '@blocksuite/global/utils'; import { assertExists } from '@blocksuite/global/utils';
@ -12,14 +12,19 @@ import {
PageIcon, PageIcon,
} from '@blocksuite/icons'; } from '@blocksuite/icons';
import type { PageMeta } from '@blocksuite/store'; import type { PageMeta } from '@blocksuite/store';
import { Divider } from '@toeverything/components/divider'; import {
Menu,
MenuIcon,
MenuItem,
MenuSeparator,
} from '@toeverything/components/menu';
import { import {
useBlockSuitePageMeta, useBlockSuitePageMeta,
usePageMetaHelper, usePageMetaHelper,
} from '@toeverything/hooks/use-block-suite-page-meta'; } from '@toeverything/hooks/use-block-suite-page-meta';
import { useBlockSuiteWorkspaceHelper } from '@toeverything/hooks/use-block-suite-workspace-helper'; import { useBlockSuiteWorkspaceHelper } from '@toeverything/hooks/use-block-suite-workspace-helper';
import { useAtom, useSetAtom } from 'jotai'; import { useAtom, useSetAtom } from 'jotai';
import { useCallback, useState } from 'react'; import { useCallback, useRef, useState } from 'react';
import { applyUpdate, encodeStateAsUpdate } from 'yjs'; import { applyUpdate, encodeStateAsUpdate } from 'yjs';
import { pageSettingFamily, setPageModeAtom } from '../../../atoms'; import { pageSettingFamily, setPageModeAtom } from '../../../atoms';
@ -34,9 +39,10 @@ type PageMenuProps = {
rename?: () => void; rename?: () => void;
pageId: string; pageId: string;
}; };
// fixme: refactor this file
export const PageMenu = ({ rename, pageId }: PageMenuProps) => { export const PageMenu = ({ rename, pageId }: PageMenuProps) => {
const t = useAFFiNEI18N(); const t = useAFFiNEI18N();
const ref = useRef(null);
// fixme(himself65): remove these hooks ASAP // fixme(himself65): remove these hooks ASAP
const [workspace] = useCurrentWorkspace(); const [workspace] = useCurrentWorkspace();
@ -105,17 +111,25 @@ export const PageMenu = ({ rename, pageId }: PageMenuProps) => {
const EditMenu = ( const EditMenu = (
<> <>
<MenuItem <MenuItem
icon={<EditIcon />} preFix={
<MenuIcon>
<EditIcon />
</MenuIcon>
}
data-testid="editor-option-menu-rename" data-testid="editor-option-menu-rename"
onClick={rename} onSelect={rename}
style={menuItemStyle} style={menuItemStyle}
> >
{t['Rename']()} {t['Rename']()}
</MenuItem> </MenuItem>
<MenuItem <MenuItem
icon={mode === 'page' ? <EdgelessIcon /> : <PageIcon />} preFix={
<MenuIcon>
{mode === 'page' ? <EdgelessIcon /> : <PageIcon />}
</MenuIcon>
}
data-testid="editor-option-menu-edgeless" data-testid="editor-option-menu-edgeless"
onClick={handleSwitchMode} onSelect={handleSwitchMode}
style={menuItemStyle} style={menuItemStyle}
> >
{t['Convert to ']()} {t['Convert to ']()}
@ -123,14 +137,16 @@ export const PageMenu = ({ rename, pageId }: PageMenuProps) => {
</MenuItem> </MenuItem>
<MenuItem <MenuItem
data-testid="editor-option-menu-favorite" data-testid="editor-option-menu-favorite"
onClick={handleFavorite} onSelect={handleFavorite}
style={menuItemStyle} style={menuItemStyle}
icon={ preFix={
favorite ? ( <MenuIcon>
<FavoritedIcon style={{ color: 'var(--affine-primary-color)' }} /> {favorite ? (
) : ( <FavoritedIcon style={{ color: 'var(--affine-primary-color)' }} />
<FavoriteIcon /> ) : (
) <FavoriteIcon />
)}
</MenuIcon>
} }
> >
{favorite ? t['Remove from favorites']() : t['Add to Favorites']()} {favorite ? t['Remove from favorites']() : t['Add to Favorites']()}
@ -144,28 +160,36 @@ export const PageMenu = ({ rename, pageId }: PageMenuProps) => {
> >
{t['com.affine.header.option.add-tag']()} {t['com.affine.header.option.add-tag']()}
</MenuItem> */} </MenuItem> */}
<Divider size="thinner" dividerColor="var(--affine-border-color)" /> <MenuSeparator />
<MenuItem <MenuItem
icon={<DuplicateIcon />} preFix={
<MenuIcon>
<DuplicateIcon />
</MenuIcon>
}
data-testid="editor-option-menu-duplicate" data-testid="editor-option-menu-duplicate"
onClick={duplicate} onSelect={duplicate}
style={menuItemStyle} style={menuItemStyle}
> >
{t['com.affine.header.option.duplicate']()} {t['com.affine.header.option.duplicate']()}
</MenuItem> </MenuItem>
<MenuItem <MenuItem
icon={<ImportIcon />} preFix={
<MenuIcon>
<ImportIcon />
</MenuIcon>
}
data-testid="editor-option-menu-import" data-testid="editor-option-menu-import"
onClick={importFile} onSelect={importFile}
style={menuItemStyle} style={menuItemStyle}
> >
{t['Import']()} {t['Import']()}
</MenuItem> </MenuItem>
<Export /> <Export />
<Divider size="thinner" dividerColor="var(--affine-border-color)" /> <MenuSeparator />
<MoveToTrash <MoveToTrash
data-testid="editor-option-menu-delete" data-testid="editor-option-menu-delete"
onItemClick={() => { onSelect={() => {
setOpenConfirm(true); setOpenConfirm(true);
}} }}
/> />
@ -176,21 +200,19 @@ export const PageMenu = ({ rename, pageId }: PageMenuProps) => {
} }
return ( return (
<> <>
<FlexWrapper alignItems="center" justifyContent="center"> <FlexWrapper alignItems="center" justifyContent="center" ref={ref}>
<Menu <Menu
content={EditMenu} items={EditMenu}
placement="bottom-end" portalOptions={{
disablePortal={true} container: ref.current,
trigger="click"
menuStyles={{
borderRadius: '8px',
padding: '8px',
background: 'var(--affine-background-overlay-panel-color)',
}} }}
// menuStyles={{
// borderRadius: '8px',
// padding: '8px',
// background: 'var(--affine-background-overlay-panel-color)',
// }}
> >
<div> <HeaderDropDownButton />
<HeaderDropDownButton />
</div>
</Menu> </Menu>
<MoveToTrash.ConfirmModal <MoveToTrash.ConfirmModal
open={openConfirm} open={openConfirm}

View File

@ -1,38 +1,28 @@
import { styled } from '@affine/component';
import { ArrowDownSmallIcon } from '@blocksuite/icons'; import { ArrowDownSmallIcon } from '@blocksuite/icons';
import { import {
IconButton, IconButton,
type IconButtonProps, type IconButtonProps,
} from '@toeverything/components/button'; } from '@toeverything/components/button';
import { forwardRef } from 'react';
const StyledIconButtonWithAnimate = styled(IconButton)(() => { import { headerMenuTrigger } from './styles.css';
return {
svg: {
transition: 'transform 0.15s ease-in-out',
},
':hover': {
svg: {
transform: 'translateY(3px)',
},
backgroundColor: 'transparent !important',
},
};
});
// fixme(himself65): need to refactor export const HeaderDropDownButton = forwardRef<
export const HeaderDropDownButton = ({ HTMLButtonElement,
onClick, Omit<IconButtonProps, 'children'>
...props >((props, ref) => {
}: Omit<IconButtonProps, 'children'>) => {
return ( return (
<StyledIconButtonWithAnimate <IconButton
data-testid="header-dropDownButton" ref={ref}
{...props} {...props}
onClick={e => { data-testid="header-dropDownButton"
onClick?.(e); className={headerMenuTrigger}
}} withoutHoverStyle={true}
type="plain"
> >
<ArrowDownSmallIcon /> <ArrowDownSmallIcon />
</StyledIconButtonWithAnimate> </IconButton>
); );
}; });
HeaderDropDownButton.displayName = 'HeaderDropDownButton';

View File

@ -0,0 +1,10 @@
import { globalStyle, style } from '@vanilla-extract/css';
export const headerMenuTrigger = style({});
globalStyle(`${headerMenuTrigger} svg`, {
transition: 'transform 0.15s ease-in-out',
});
globalStyle(`${headerMenuTrigger}:hover svg`, {
transform: 'translateY(3px)',
});

View File

@ -1,4 +1,3 @@
import { Menu, MenuItem } from '@affine/component';
import { WorkspaceList } from '@affine/component/workspace-list'; import { WorkspaceList } from '@affine/component/workspace-list';
import type { import type {
AffineCloudWorkspace, AffineCloudWorkspace,
@ -19,6 +18,7 @@ import type { DragEndEvent } from '@dnd-kit/core';
import { Popover } from '@mui/material'; import { Popover } from '@mui/material';
import { IconButton } from '@toeverything/components/button'; import { IconButton } from '@toeverything/components/button';
import { Divider } from '@toeverything/components/divider'; import { Divider } from '@toeverything/components/divider';
import { Menu, MenuIcon, MenuItem } from '@toeverything/components/menu';
import { useSetAtom } from 'jotai'; import { useSetAtom } from 'jotai';
// eslint-disable-next-line @typescript-eslint/no-restricted-imports // eslint-disable-next-line @typescript-eslint/no-restricted-imports
import { signOut, useSession } from 'next-auth/react'; import { signOut, useSession } from 'next-auth/react';
@ -35,6 +35,7 @@ import {
StyledCreateWorkspaceCardPillContent, StyledCreateWorkspaceCardPillContent,
StyledCreateWorkspaceCardPillIcon, StyledCreateWorkspaceCardPillIcon,
StyledImportWorkspaceCardPill, StyledImportWorkspaceCardPill,
StyledItem,
StyledModalBody, StyledModalBody,
StyledModalContent, StyledModalContent,
StyledModalFooterContent, StyledModalFooterContent,
@ -68,13 +69,12 @@ const AccountMenu = () => {
const setOpen = useSetAtom(openSettingModalAtom); const setOpen = useSetAtom(openSettingModalAtom);
return ( return (
<div> <div>
{/* <div>Unlimted</div>
<Divider size="thinner" dividerColor="var(--affine-border-color)" />
<MenuItem icon={<ImportIcon />} data-testid="editor-option-menu-import">
{t['com.affine.workspace.cloud.join']()}
</MenuItem> */}
<MenuItem <MenuItem
icon={<AccountIcon />} preFix={
<MenuIcon>
<AccountIcon />
</MenuIcon>
}
data-testid="editor-option-menu-import" data-testid="editor-option-menu-import"
onClick={useCallback(() => { onClick={useCallback(() => {
setOpen(prev => ({ ...prev, open: true, activeTab: 'account' })); setOpen(prev => ({ ...prev, open: true, activeTab: 'account' }));
@ -84,7 +84,11 @@ const AccountMenu = () => {
</MenuItem> </MenuItem>
<Divider /> <Divider />
<MenuItem <MenuItem
icon={<SignOutIcon />} preFix={
<MenuIcon>
<SignOutIcon />
</MenuIcon>
}
data-testid="editor-option-menu-import" data-testid="editor-option-menu-import"
onClick={useCallback(() => { onClick={useCallback(() => {
signOut().catch(console.error); signOut().catch(console.error);
@ -188,11 +192,7 @@ export const WorkspaceListModal = ({
{!isLoggedIn ? ( {!isLoggedIn ? (
<StyledModalHeaderContent> <StyledModalHeaderContent>
<StyledSignInCardPill> <StyledSignInCardPill>
<MenuItem <StyledItem
style={{
height: 'auto',
padding: '0px 12px',
}}
onClick={async () => { onClick={async () => {
if (!runtimeConfig.enableCloud) { if (!runtimeConfig.enableCloud) {
setDisableCloudOpen(true); setDisableCloudOpen(true);
@ -218,7 +218,7 @@ export const WorkspaceListModal = ({
</StyledSignInCardPillTextSecondary> </StyledSignInCardPillTextSecondary>
</StyledSignInCardPillTextCotainer> </StyledSignInCardPillTextCotainer>
</StyledCreateWorkspaceCardPillContent> </StyledCreateWorkspaceCardPillContent>
</MenuItem> </StyledItem>
</StyledSignInCardPill> </StyledSignInCardPill>
<Divider style={{ margin: '12px 0px' }} /> <Divider style={{ margin: '12px 0px' }} />
</StyledModalHeaderContent> </StyledModalHeaderContent>
@ -227,12 +227,7 @@ export const WorkspaceListModal = ({
<StyledModalHeader> <StyledModalHeader>
<StyledModalTitle>{session?.user.email}</StyledModalTitle> <StyledModalTitle>{session?.user.email}</StyledModalTitle>
<StyledOperationWrapper> <StyledOperationWrapper>
<Menu <Menu items={<AccountMenu />}>
placement="bottom-end"
trigger={['click']}
content={<AccountMenu />}
zIndex={1000}
>
<IconButton <IconButton
data-testid="more-button" data-testid="more-button"
icon={<MoreHorizontalIcon />} icon={<MoreHorizontalIcon />}
@ -287,14 +282,7 @@ export const WorkspaceListModal = ({
</StyledModalContent> </StyledModalContent>
{runtimeConfig.enableSQLiteProvider && environment.isDesktop ? ( {runtimeConfig.enableSQLiteProvider && environment.isDesktop ? (
<StyledImportWorkspaceCardPill> <StyledImportWorkspaceCardPill>
<MenuItem <StyledItem onClick={onAddWorkspace} data-testid="add-workspace">
onClick={onAddWorkspace}
data-testid="add-workspace"
style={{
height: 'auto',
padding: '8px 12px',
}}
>
<StyledCreateWorkspaceCardPillContent> <StyledCreateWorkspaceCardPillContent>
<StyledCreateWorkspaceCardPillIcon> <StyledCreateWorkspaceCardPillIcon>
<ImportIcon /> <ImportIcon />
@ -303,20 +291,13 @@ export const WorkspaceListModal = ({
<p>{t['com.affine.workspace.local.import']()}</p> <p>{t['com.affine.workspace.local.import']()}</p>
</div> </div>
</StyledCreateWorkspaceCardPillContent> </StyledCreateWorkspaceCardPillContent>
</MenuItem> </StyledItem>
</StyledImportWorkspaceCardPill> </StyledImportWorkspaceCardPill>
) : null} ) : null}
</StyledModalBody> </StyledModalBody>
<StyledModalFooterContent> <StyledModalFooterContent>
<StyledCreateWorkspaceCardPill> <StyledCreateWorkspaceCardPill>
<MenuItem <StyledItem onClick={onNewWorkspace} data-testid="new-workspace">
style={{
height: 'auto',
padding: '8px 12px',
}}
onClick={onNewWorkspace}
data-testid="new-workspace"
>
<StyledCreateWorkspaceCardPillContent> <StyledCreateWorkspaceCardPillContent>
<StyledCreateWorkspaceCardPillIcon> <StyledCreateWorkspaceCardPillIcon>
<PlusIcon /> <PlusIcon />
@ -325,7 +306,7 @@ export const WorkspaceListModal = ({
<p>{t['New Workspace']()}</p> <p>{t['New Workspace']()}</p>
</div> </div>
</StyledCreateWorkspaceCardPillContent> </StyledCreateWorkspaceCardPillContent>
</MenuItem> </StyledItem>
</StyledCreateWorkspaceCardPill> </StyledCreateWorkspaceCardPill>
</StyledModalFooterContent> </StyledModalFooterContent>
</Popover> </Popover>

View File

@ -241,3 +241,32 @@ export const StyledWorkspaceFlavourTitle = styled('div')(() => {
marginBottom: '4px', marginBottom: '4px',
}; };
}); });
export const StyledItem = styled('button')<{
active?: boolean;
}>(({ active = false }) => {
return {
height: 'auto',
padding: '8px 12px',
width: '100%',
borderRadius: '5px',
fontSize: 'var(--affine-font-sm)',
...displayFlex('flex-start', 'center'),
cursor: 'pointer',
position: 'relative',
backgroundColor: 'transparent',
color: 'var(--affine-text-primary-color)',
svg: {
color: 'var(--affine-icon-color)',
},
':hover': {
backgroundColor: 'var(--affine-hover-color)',
},
...(active
? {
backgroundColor: 'var(--affine-hover-color)',
}
: {}),
};
});

View File

@ -1,5 +1,4 @@
import { Menu } from '@affine/component'; import { MenuItem as CollectionItem } from '@affine/component/app-sidebar';
import { MenuItem } from '@affine/component/app-sidebar';
import { import {
EditCollectionModel, EditCollectionModel,
useCollectionManager, useCollectionManager,
@ -21,6 +20,13 @@ import type { PageMeta, Workspace } from '@blocksuite/store';
import type { DragEndEvent } from '@dnd-kit/core'; import type { DragEndEvent } from '@dnd-kit/core';
import { useDroppable } from '@dnd-kit/core'; import { useDroppable } from '@dnd-kit/core';
import * as Collapsible from '@radix-ui/react-collapsible'; import * as Collapsible from '@radix-ui/react-collapsible';
import { IconButton } from '@toeverything/components/button';
import {
Menu,
MenuIcon,
MenuItem,
type MenuItemProps,
} from '@toeverything/components/menu';
import { useBlockSuitePageMeta } from '@toeverything/hooks/use-block-suite-page-meta'; import { useBlockSuitePageMeta } from '@toeverything/hooks/use-block-suite-page-meta';
import type { ReactElement } from 'react'; import type { ReactElement } from 'react';
import React, { useCallback, useMemo, useState } from 'react'; import React, { useCallback, useMemo, useState } from 'react';
@ -60,7 +66,7 @@ const CollectionOperations = ({
icon: ReactElement; icon: ReactElement;
name: string; name: string;
click: () => void; click: () => void;
className?: string; type?: MenuItemProps['type'];
element?: undefined; element?: undefined;
} }
| { | {
@ -70,12 +76,20 @@ const CollectionOperations = ({
>( >(
() => [ () => [
{ {
icon: <FilterIcon />, icon: (
<MenuIcon>
<FilterIcon />
</MenuIcon>
),
name: t['Edit Filter'](), name: t['Edit Filter'](),
click: showUpdateCollection, click: showUpdateCollection,
}, },
{ {
icon: <UnpinIcon />, icon: (
<MenuIcon>
<UnpinIcon />
</MenuIcon>
),
name: t['Unpin'](), name: t['Unpin'](),
click: () => { click: () => {
return setting.updateCollection({ return setting.updateCollection({
@ -88,12 +102,16 @@ const CollectionOperations = ({
element: <div key="divider" className={styles.menuDividerStyle}></div>, element: <div key="divider" className={styles.menuDividerStyle}></div>,
}, },
{ {
icon: <DeleteIcon />, icon: (
<MenuIcon>
<DeleteIcon />
</MenuIcon>
),
name: t['Delete'](), name: t['Delete'](),
click: () => { click: () => {
return setting.deleteCollection(view.id); return setting.deleteCollection(view.id);
}, },
className: styles.deleteFolder, type: 'danger',
}, },
], ],
[setting, showUpdateCollection, t, view] [setting, showUpdateCollection, t, view]
@ -108,8 +126,8 @@ const CollectionOperations = ({
<MenuItem <MenuItem
data-testid="collection-option" data-testid="collection-option"
key={action.name} key={action.name}
className={action.className} type={action.type}
icon={action.icon} preFix={action.icon}
onClick={action.click} onClick={action.click}
> >
{action.name} {action.name}
@ -181,6 +199,7 @@ const CollectionRenderer = ({
const pagesToRender = pages.filter( const pagesToRender = pages.filter(
page => filterPage(collection, page) && !page.trash page => filterPage(collection, page) && !page.trash
); );
return ( return (
<Collapsible.Root open={!collapsed}> <Collapsible.Root open={!collapsed}>
<EditCollectionModel <EditCollectionModel
@ -191,7 +210,7 @@ const CollectionRenderer = ({
open={show} open={show}
onClose={() => showUpdateCollection(false)} onClose={() => showUpdateCollection(false)}
/> />
<MenuItem <CollectionItem
data-testid="collection-item" data-testid="collection-item"
data-type="collection-list-item" data-type="collection-list-item"
ref={setNodeRef} ref={setNodeRef}
@ -200,9 +219,7 @@ const CollectionRenderer = ({
icon={<ViewLayersIcon />} icon={<ViewLayersIcon />}
postfix={ postfix={
<Menu <Menu
trigger="click" items={
placement="bottom-start"
content={
<CollectionOperations <CollectionOperations
view={collection} view={collection}
showUpdateCollection={() => showUpdateCollection(true)} showUpdateCollection={() => showUpdateCollection(true)}
@ -210,9 +227,13 @@ const CollectionRenderer = ({
/> />
} }
> >
<div data-testid="collection-options" className={styles.more}> <IconButton
data-testid="collection-options"
type="plain"
withoutHoverStyle
>
<MoreHorizontalIcon /> <MoreHorizontalIcon />
</div> </IconButton>
</Menu> </Menu>
} }
collapsed={pagesToRender.length > 0 ? collapsed : undefined} collapsed={pagesToRender.length > 0 ? collapsed : undefined}
@ -227,7 +248,7 @@ const CollectionRenderer = ({
> >
<div>{collection.name}</div> <div>{collection.name}</div>
</div> </div>
</MenuItem> </CollectionItem>
<Collapsible.Content className={styles.collapsibleContent}> <Collapsible.Content className={styles.collapsibleContent}>
<div style={{ marginLeft: 20, marginTop: -4 }}> <div style={{ marginLeft: 20, marginTop: -4 }}>
{pagesToRender.map(page => { {pagesToRender.map(page => {
@ -260,13 +281,13 @@ export const CollectionsList = ({ workspace }: CollectionsListProps) => {
const t = useAFFiNEI18N(); const t = useAFFiNEI18N();
if (pinedCollections.length === 0) { if (pinedCollections.length === 0) {
return ( return (
<MenuItem <CollectionItem
data-testid="slider-bar-collection-null-description" data-testid="slider-bar-collection-null-description"
icon={<InformationIcon />} icon={<InformationIcon />}
disabled disabled
> >
<span>{t['Create a collection']()}</span> <span>{t['Create a collection']()}</span>
</MenuItem> </CollectionItem>
); );
} }
return ( return (

View File

@ -1,5 +1,4 @@
import { Menu } from '@affine/component'; import { MenuItem as CollectionItem } from '@affine/component/app-sidebar';
import { MenuItem } from '@affine/component/app-sidebar';
import { useAFFiNEI18N } from '@affine/i18n/hooks'; import { useAFFiNEI18N } from '@affine/i18n/hooks';
import { import {
DeleteIcon, DeleteIcon,
@ -11,6 +10,13 @@ import {
} from '@blocksuite/icons'; } from '@blocksuite/icons';
import type { PageMeta, Workspace } from '@blocksuite/store'; import type { PageMeta, Workspace } from '@blocksuite/store';
import * as Collapsible from '@radix-ui/react-collapsible'; import * as Collapsible from '@radix-ui/react-collapsible';
import { IconButton } from '@toeverything/components/button';
import {
Menu,
MenuIcon,
MenuItem,
type MenuItemProps,
} from '@toeverything/components/menu';
import { useBlockSuitePageReferences } from '@toeverything/hooks/use-block-suite-page-references'; import { useBlockSuitePageReferences } from '@toeverything/hooks/use-block-suite-page-references';
import { useAtomValue } from 'jotai/index'; import { useAtomValue } from 'jotai/index';
import type { ReactElement } from 'react'; import type { ReactElement } from 'react';
@ -46,7 +52,7 @@ export const PageOperations = ({
icon: ReactElement; icon: ReactElement;
name: string; name: string;
click: () => void; click: () => void;
className?: string; type?: MenuItemProps['type'];
element?: undefined; element?: undefined;
} }
| { | {
@ -58,7 +64,11 @@ export const PageOperations = ({
...(inAllowList ...(inAllowList
? [ ? [
{ {
icon: <FilterMinusIcon />, icon: (
<MenuIcon>
<FilterMinusIcon />
</MenuIcon>
),
name: t['Remove special filter'](), name: t['Remove special filter'](),
click: () => removeFromAllowList(page.id), click: () => removeFromAllowList(page.id),
}, },
@ -67,7 +77,11 @@ export const PageOperations = ({
...(!inExcludeList ...(!inExcludeList
? [ ? [
{ {
icon: <FilterUndoIcon />, icon: (
<MenuIcon>
<FilterUndoIcon />
</MenuIcon>
),
name: t['Exclude from filter'](), name: t['Exclude from filter'](),
click: () => addToExcludeList(page.id), click: () => addToExcludeList(page.id),
}, },
@ -77,12 +91,16 @@ export const PageOperations = ({
element: <div key="divider" className={styles.menuDividerStyle}></div>, element: <div key="divider" className={styles.menuDividerStyle}></div>,
}, },
{ {
icon: <DeleteIcon />, icon: (
<MenuIcon>
<DeleteIcon />
</MenuIcon>
),
name: t['Delete'](), name: t['Delete'](),
click: () => { click: () => {
removeToTrash(page.id); removeToTrash(page.id);
}, },
className: styles.deleteFolder, type: 'danger',
}, },
], ],
[ [
@ -105,8 +123,8 @@ export const PageOperations = ({
<MenuItem <MenuItem
data-testid="collection-page-option" data-testid="collection-page-option"
key={action.name} key={action.name}
className={action.className} type={action.type}
icon={action.icon} preFix={action.icon}
onClick={action.click} onClick={action.click}
> >
{action.name} {action.name}
@ -133,6 +151,7 @@ export const Page = ({
workspace: Workspace; workspace: Workspace;
allPageMeta: Record<string, PageMeta>; allPageMeta: Record<string, PageMeta>;
}) => { }) => {
const ref = React.useRef(null);
const [collapsed, setCollapsed] = React.useState(true); const [collapsed, setCollapsed] = React.useState(true);
const params = useParams(); const params = useParams();
const { jumpToPage } = useNavigateHelper(); const { jumpToPage } = useNavigateHelper();
@ -148,7 +167,7 @@ export const Page = ({
const referencesToRender = references.filter(id => !allPageMeta[id]?.trash); const referencesToRender = references.filter(id => !allPageMeta[id]?.trash);
return ( return (
<Collapsible.Root open={!collapsed}> <Collapsible.Root open={!collapsed}>
<MenuItem <CollectionItem
data-testid="collection-page" data-testid="collection-page"
data-type="collection-list-item" data-type="collection-list-item"
icon={icon} icon={icon}
@ -157,31 +176,35 @@ export const Page = ({
active={active} active={active}
collapsed={referencesToRender.length > 0 ? collapsed : undefined} collapsed={referencesToRender.length > 0 ? collapsed : undefined}
onCollapsedChange={setCollapsed} onCollapsedChange={setCollapsed}
ref={ref}
postfix={ postfix={
<Menu <Menu
trigger="click" items={
placement="bottom-start" <PageOperations
content={ inAllowList={inAllowList}
<div style={{ width: 220 }}> removeFromAllowList={removeFromAllowList}
<PageOperations inExcludeList={inExcludeList}
inAllowList={inAllowList} addToExcludeList={addToExcludeList}
removeFromAllowList={removeFromAllowList} page={page}
inExcludeList={inExcludeList} workspace={workspace}
addToExcludeList={addToExcludeList} />
page={page}
workspace={workspace}
/>
</div>
} }
portalOptions={{
container: ref.current,
}}
> >
<div data-testid="collection-page-options" className={styles.more}> <IconButton
<MoreHorizontalIcon></MoreHorizontalIcon> data-testid="collection-page-options"
</div> type="plain"
withoutHoverStyle
>
<MoreHorizontalIcon />
</IconButton>
</Menu> </Menu>
} }
> >
{page.title || t['Untitled']()} {page.title || t['Untitled']()}
</MenuItem> </CollectionItem>
<Collapsible.Content className={styles.collapsibleContent}> <Collapsible.Content className={styles.collapsibleContent}>
{referencesToRender.map(id => { {referencesToRender.map(id => {
return ( return (

View File

@ -22,7 +22,7 @@
"@blocksuite/blocks": "0.0.0-20230829150056-df43987c-nightly", "@blocksuite/blocks": "0.0.0-20230829150056-df43987c-nightly",
"@blocksuite/editor": "0.0.0-20230829150056-df43987c-nightly", "@blocksuite/editor": "0.0.0-20230829150056-df43987c-nightly",
"@blocksuite/global": "0.0.0-20230829150056-df43987c-nightly", "@blocksuite/global": "0.0.0-20230829150056-df43987c-nightly",
"@blocksuite/icons": "^2.1.31", "@blocksuite/icons": "^2.1.32",
"@blocksuite/lit": "0.0.0-20230829150056-df43987c-nightly", "@blocksuite/lit": "0.0.0-20230829150056-df43987c-nightly",
"@blocksuite/store": "0.0.0-20230829150056-df43987c-nightly", "@blocksuite/store": "0.0.0-20230829150056-df43987c-nightly",
"@toeverything/hooks": "workspace:*", "@toeverything/hooks": "workspace:*",

View File

@ -35,7 +35,7 @@
"@blocksuite/blocks": "0.0.0-20230829150056-df43987c-nightly", "@blocksuite/blocks": "0.0.0-20230829150056-df43987c-nightly",
"@blocksuite/editor": "0.0.0-20230829150056-df43987c-nightly", "@blocksuite/editor": "0.0.0-20230829150056-df43987c-nightly",
"@blocksuite/global": "0.0.0-20230829150056-df43987c-nightly", "@blocksuite/global": "0.0.0-20230829150056-df43987c-nightly",
"@blocksuite/icons": "^2.1.31", "@blocksuite/icons": "^2.1.32",
"@blocksuite/lit": "0.0.0-20230829150056-df43987c-nightly", "@blocksuite/lit": "0.0.0-20230829150056-df43987c-nightly",
"@blocksuite/store": "0.0.0-20230829150056-df43987c-nightly", "@blocksuite/store": "0.0.0-20230829150056-df43987c-nightly",
"@tomfreudenberg/next-auth-mock": "^0.5.6", "@tomfreudenberg/next-auth-mock": "^0.5.6",

View File

@ -55,7 +55,7 @@
"@blocksuite/blocks": "0.0.0-20230829150056-df43987c-nightly", "@blocksuite/blocks": "0.0.0-20230829150056-df43987c-nightly",
"@blocksuite/editor": "0.0.0-20230829150056-df43987c-nightly", "@blocksuite/editor": "0.0.0-20230829150056-df43987c-nightly",
"@blocksuite/global": "0.0.0-20230829150056-df43987c-nightly", "@blocksuite/global": "0.0.0-20230829150056-df43987c-nightly",
"@blocksuite/icons": "^2.1.31", "@blocksuite/icons": "^2.1.32",
"@blocksuite/lit": "0.0.0-20230829150056-df43987c-nightly", "@blocksuite/lit": "0.0.0-20230829150056-df43987c-nightly",
"@blocksuite/store": "0.0.0-20230829150056-df43987c-nightly", "@blocksuite/store": "0.0.0-20230829150056-df43987c-nightly",
"@types/react": "^18.2.21", "@types/react": "^18.2.21",

View File

@ -62,10 +62,10 @@ export const content = style({
export const postfix = style({ export const postfix = style({
justifySelf: 'flex-end', justifySelf: 'flex-end',
display: 'none', visibility: 'hidden',
selectors: { selectors: {
[`${root}:hover &`]: { [`${root}:hover &`]: {
display: 'flex', visibility: 'visible',
}, },
}, },
}); });

View File

@ -0,0 +1,54 @@
import { style } from '@vanilla-extract/css';
export const divider = style({
width: '0.5px',
height: '16px',
background: 'var(--affine-divider-color)',
// fix dropdown button click area
margin: '0 4px',
marginRight: 0,
});
export const dropdownWrapper = style({
width: '100%',
height: '100%',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
paddingLeft: '4px',
paddingRight: '10px',
});
export const dropdownIcon = style({
borderRadius: '4px',
selectors: {
[`${dropdownWrapper}:hover &`]: {
background: 'var(--affine-hover-color)',
},
},
});
export const dropdownBtn = style({
display: 'inline-flex',
alignItems: 'center',
justifyContent: 'center',
padding: '0 10px',
// fix dropdown button click area
paddingRight: 0,
color: 'var(--affine-text-primary-color)',
fontWeight: 600,
background: 'var(--affine-button-gray-color)',
boxShadow: 'var(--affine-float-button-shadow)',
borderRadius: '8px',
fontSize: 'var(--affine-font-sm)',
// width: '100%',
height: '32px',
userSelect: 'none',
whiteSpace: 'nowrap',
cursor: 'pointer',
selectors: {
'&:hover': {
background: 'var(--affine-hover-color-filled)',
},
},
});

View File

@ -0,0 +1,36 @@
import { ArrowDownSmallIcon } from '@blocksuite/icons';
import {
type ButtonHTMLAttributes,
forwardRef,
type MouseEventHandler,
} from 'react';
import * as styles from './dropdown.css';
type DropdownButtonProps = {
onClickDropDown?: MouseEventHandler<HTMLElement>;
} & ButtonHTMLAttributes<HTMLButtonElement>;
export const DropdownButton = forwardRef<
HTMLButtonElement,
DropdownButtonProps
>(({ onClickDropDown, children, ...props }, ref) => {
const handleClickDropDown: MouseEventHandler<HTMLElement> = e => {
e.stopPropagation();
onClickDropDown?.(e);
};
return (
<button ref={ref} className={styles.dropdownBtn} {...props}>
<span>{children}</span>
<span className={styles.divider} />
<span className={styles.dropdownWrapper} onClick={handleClickDropDown}>
<ArrowDownSmallIcon
className={styles.dropdownIcon}
width={16}
height={16}
/>
</span>
</button>
);
});
DropdownButton.displayName = 'DropdownButton';

View File

@ -1,10 +1,11 @@
import { useAFFiNEI18N } from '@affine/i18n/hooks'; import { useAFFiNEI18N } from '@affine/i18n/hooks';
import { EdgelessIcon, ImportIcon, PageIcon } from '@blocksuite/icons'; import { EdgelessIcon, ImportIcon, PageIcon } from '@blocksuite/icons';
import { Menu } from '@toeverything/components/menu';
import { useState } from 'react'; import { useState } from 'react';
import { DropdownButton } from '../../../ui/button/dropdown'; // import { Menu } from '../../../ui/menu/menu';
import { Menu } from '../../../ui/menu/menu';
import { BlockCard } from '../../card/block-card'; import { BlockCard } from '../../card/block-card';
import { DropdownButton } from './dropdown';
type NewPageButtonProps = { type NewPageButtonProps = {
createNewPage: () => void; createNewPage: () => void;
@ -32,18 +33,21 @@ export const CreateNewPagePopup = ({
desc={t['com.affine.write_with_a_blank_page']()} desc={t['com.affine.write_with_a_blank_page']()}
right={<PageIcon width={20} height={20} />} right={<PageIcon width={20} height={20} />}
onClick={createNewPage} onClick={createNewPage}
data-testid="new-page-button-in-all-page"
/> />
<BlockCard <BlockCard
title={t['com.affine.new_edgeless']()} title={t['com.affine.new_edgeless']()}
desc={t['com.affine.draw_with_a_blank_whiteboard']()} desc={t['com.affine.draw_with_a_blank_whiteboard']()}
right={<EdgelessIcon width={20} height={20} />} right={<EdgelessIcon width={20} height={20} />}
onClick={createNewEdgeless} onClick={createNewEdgeless}
data-testid="new-edgeless-button-in-all-page"
/> />
<BlockCard <BlockCard
title={t['com.affine.new_import']()} title={t['com.affine.new_import']()}
desc={t['com.affine.import_file']()} desc={t['com.affine.import_file']()}
right={<ImportIcon width={20} height={20} />} right={<ImportIcon width={20} height={20} />}
onClick={importFile} onClick={importFile}
data-testid="import-button-in-all-page"
/> />
{/* TODO Import */} {/* TODO Import */}
</div> </div>
@ -59,18 +63,10 @@ export const NewPageButton = ({
const [open, setOpen] = useState(false); const [open, setOpen] = useState(false);
return ( return (
<Menu <Menu
visible={open} rootOptions={{
placement="bottom-end" open,
trigger={['click']}
disablePortal={true}
onClickAway={() => {
setOpen(false);
}} }}
menuStyles={{ items={
padding: '0px',
background: 'var(--affine-background-overlay-panel-color)',
}}
content={
<CreateNewPagePopup <CreateNewPagePopup
createNewPage={() => { createNewPage={() => {
createNewPage(); createNewPage();

View File

@ -1,8 +1,9 @@
import type { Tag } from '@affine/env/filter'; import type { Tag } from '@affine/env/filter';
import { Menu } from '@toeverything/components/menu';
import Menu from '../../../ui/menu/menu';
import * as styles from './tags.css'; import * as styles from './tags.css';
// fixme: This component should use popover instead of menu
export const Tags = ({ value }: { value: Tag[] }) => { export const Tags = ({ value }: { value: Tag[] }) => {
const list = value.map(tag => { const list = value.map(tag => {
return ( return (
@ -16,10 +17,7 @@ export const Tags = ({ value }: { value: Tag[] }) => {
); );
}); });
return ( return (
<Menu <Menu items={<div className={styles.tagListFull}>{list}</div>}>
pointerEnterDelay={500}
content={<div className={styles.tagListFull}>{list}</div>}
>
<div className={styles.tagList}>{list}</div> <div className={styles.tagList}>{list}</div>
</Menu> </Menu>
); );

View File

@ -1,9 +1,9 @@
import type { Filter, Literal } from '@affine/env/filter'; import type { Filter, Literal } from '@affine/env/filter';
import type { PropertiesMeta } from '@affine/env/filter'; import type { PropertiesMeta } from '@affine/env/filter';
import { Menu, MenuItem } from '@toeverything/components/menu';
import type { ReactNode } from 'react'; import type { ReactNode } from 'react';
import { useMemo } from 'react'; import { useMemo } from 'react';
import { Menu, MenuItem } from '../../../ui/menu';
import { FilterTag } from './filter-tag-translation'; import { FilterTag } from './filter-tag-translation';
import * as styles from './index.css'; import * as styles from './index.css';
import { literalMatcher } from './literal-matcher'; import { literalMatcher } from './literal-matcher';
@ -50,8 +50,7 @@ export const Condition = ({
style={{ display: 'flex', userSelect: 'none', alignItems: 'center' }} style={{ display: 'flex', userSelect: 'none', alignItems: 'center' }}
> >
<Menu <Menu
trigger="click" items={
content={
<VariableSelect <VariableSelect
propertiesMeta={propertiesMeta} propertiesMeta={propertiesMeta}
selected={[]} selected={[]}
@ -67,8 +66,7 @@ export const Condition = ({
</div> </div>
</Menu> </Menu>
<Menu <Menu
trigger="click" items={
content={
<FunctionSelect <FunctionSelect
propertiesMeta={propertiesMeta} propertiesMeta={propertiesMeta}
value={value} value={value}

View File

@ -4,8 +4,8 @@ import { useAFFiNEI18N } from '@affine/i18n/hooks';
import { CloseIcon, PlusIcon } from '@blocksuite/icons'; import { CloseIcon, PlusIcon } from '@blocksuite/icons';
import { Button } from '@toeverything/components/button'; import { Button } from '@toeverything/components/button';
import { IconButton } from '@toeverything/components/button'; import { IconButton } from '@toeverything/components/button';
import { Menu } from '@toeverything/components/menu';
import { Menu } from '../../..';
import { Condition } from './condition'; import { Condition } from './condition';
import * as styles from './index.css'; import * as styles from './index.css';
import { CreateFilterMenu } from './vars'; import { CreateFilterMenu } from './vars';
@ -53,8 +53,7 @@ export const FilterList = ({
); );
})} })}
<Menu <Menu
trigger={'click'} items={
content={
<CreateFilterMenu <CreateFilterMenu
value={value} value={value}
onChange={onChange} onChange={onChange}

View File

@ -4,7 +4,7 @@ type FilterTagProps = {
name: string; name: string;
}; };
export const FilterTag = ({ name }: FilterTagProps) => { const useFilterTag = ({ name }: FilterTagProps) => {
const t = useAFFiNEI18N(); const t = useAFFiNEI18N();
switch (name) { switch (name) {
case 'Created': case 'Created':
@ -41,3 +41,9 @@ export const FilterTag = ({ name }: FilterTagProps) => {
return name; return name;
} }
}; };
export const FilterTag = ({ name }: FilterTagProps) => {
const tag = useFilterTag({ name });
return <span data-testid={`filler-tag-${tag}`}>{tag}</span>;
};

View File

@ -1,8 +1,7 @@
import { DoneIcon } from '@blocksuite/icons'; import { Menu, MenuItem } from '@toeverything/components/menu';
import type { MouseEvent } from 'react'; import type { MouseEvent } from 'react';
import { useMemo } from 'react'; import { useMemo } from 'react';
import Menu from '../../../ui/menu/menu';
import * as styles from './multi-select.css'; import * as styles from './multi-select.css';
export const MultiSelect = ({ export const MultiSelect = ({
@ -21,10 +20,10 @@ export const MultiSelect = ({
() => Object.fromEntries(options.map(v => [v.value, v])), () => Object.fromEntries(options.map(v => [v.value, v])),
[options] [options]
); );
return ( return (
<Menu <Menu
trigger="click" items={
content={
<div data-testid="multi-select" className={styles.optionList}> <div data-testid="multi-select" className={styles.optionList}>
{options.map(option => { {options.map(option => {
const selected = value.includes(option.value); const selected = value.includes(option.value);
@ -37,25 +36,14 @@ export const MultiSelect = ({
} }
}; };
return ( return (
<div <MenuItem
className={styles.selectOption} data-testid={`multi-select-${option.label}`}
data-testid="select-option" checked={selected}
style={{
backgroundColor: selected
? 'var(--affine-hover-color)'
: undefined,
}}
onClick={click} onClick={click}
key={option.value} key={option.value}
> >
<div className={styles.optionLabel}>{option.label}</div> {option.label}
<div </MenuItem>
style={{ opacity: selected ? 1 : 0 }}
className={styles.done}
>
<DoneIcon />
</div>
</div>
); );
})} })}
</div> </div>

View File

@ -5,10 +5,10 @@ import type {
VariableMap, VariableMap,
} from '@affine/env/filter'; } from '@affine/env/filter';
import { useAFFiNEI18N } from '@affine/i18n/hooks'; import { useAFFiNEI18N } from '@affine/i18n/hooks';
import { MenuIcon, MenuItem } from '@toeverything/components/menu';
import dayjs from 'dayjs'; import dayjs from 'dayjs';
import type { ReactNode } from 'react'; import type { ReactNode } from 'react';
import { MenuItem } from '../../../ui/menu';
import { FilterTag } from './filter-tag-translation'; import { FilterTag } from './filter-tag-translation';
import * as styles from './index.css'; import * as styles from './index.css';
import { tBoolean, tDate, tTag } from './logical/custom-type'; import { tBoolean, tDate, tTag } from './logical/custom-type';
@ -92,7 +92,7 @@ export const VariableSelect = ({
// .filter(v => !selected.find(filter => filter.left.name === v.name)) // .filter(v => !selected.find(filter => filter.left.name === v.name))
.map(v => ( .map(v => (
<MenuItem <MenuItem
icon={variableDefineMap[v.name].icon} preFix={<MenuIcon>{variableDefineMap[v.name].icon}</MenuIcon>}
key={v.name} key={v.name}
onClick={() => { onClick={() => {
onSelect(createDefaultFilter(v, propertiesMeta)); onSelect(createDefaultFilter(v, propertiesMeta));

View File

@ -9,10 +9,11 @@ import {
ResetIcon, ResetIcon,
} from '@blocksuite/icons'; } from '@blocksuite/icons';
import { IconButton } from '@toeverything/components/button'; import { IconButton } from '@toeverything/components/button';
import { Menu, MenuIcon, MenuItem } from '@toeverything/components/menu';
import { Tooltip } from '@toeverything/components/tooltip'; import { Tooltip } from '@toeverything/components/tooltip';
import { useState } from 'react'; import { useState } from 'react';
import { Confirm, FlexWrapper, Menu, MenuItem } from '../../..'; import { Confirm, FlexWrapper } from '../../..';
import { DisablePublicSharing, MoveToTrash } from './operation-menu-items'; import { DisablePublicSharing, MoveToTrash } from './operation-menu-items';
export interface OperationCellProps { export interface OperationCellProps {
@ -43,31 +44,40 @@ export const OperationCell = ({
{isPublic && ( {isPublic && (
<DisablePublicSharing <DisablePublicSharing
data-testid="disable-public-sharing" data-testid="disable-public-sharing"
onItemClick={() => { onSelect={() => {
setOpenDisableShared(true); setOpenDisableShared(true);
}} }}
/> />
)} )}
<MenuItem <MenuItem
onClick={onToggleFavoritePage} onClick={onToggleFavoritePage}
icon={ preFix={
favorite ? ( <MenuIcon>
<FavoritedIcon style={{ color: 'var(--affine-primary-color)' }} /> {favorite ? (
) : ( <FavoritedIcon style={{ color: 'var(--affine-primary-color)' }} />
<FavoriteIcon /> ) : (
) <FavoriteIcon />
)}
</MenuIcon>
} }
> >
{favorite ? t['Remove from favorites']() : t['Add to Favorites']()} {favorite ? t['Remove from favorites']() : t['Add to Favorites']()}
</MenuItem> </MenuItem>
{!isDesktop && ( {!isDesktop && (
<MenuItem onClick={onOpenPageInNewTab} icon={<OpenInNewIcon />}> <MenuItem
onClick={onOpenPageInNewTab}
preFix={
<MenuIcon>
<OpenInNewIcon />
</MenuIcon>
}
>
{t['Open in new tab']()} {t['Open in new tab']()}
</MenuItem> </MenuItem>
)} )}
<MoveToTrash <MoveToTrash
data-testid="move-to-trash" data-testid="move-to-trash"
onItemClick={() => { onSelect={() => {
setOpen(true); setOpen(true);
}} }}
/> />
@ -76,13 +86,8 @@ export const OperationCell = ({
return ( return (
<> <>
<FlexWrapper alignItems="center" justifyContent="center"> <FlexWrapper alignItems="center" justifyContent="center">
<Menu <Menu items={OperationMenu}>
content={OperationMenu} <IconButton type="plain" data-testid="page-list-operation-button">
placement="bottom"
disablePortal={true}
trigger="click"
>
<IconButton data-testid="page-list-operation-button">
<MoreVerticalIcon /> <MoreVerticalIcon />
</IconButton> </IconButton>
</Menu> </Menu>

View File

@ -1,36 +0,0 @@
import { useAFFiNEI18N } from '@affine/i18n/hooks';
import { CopyIcon } from '@blocksuite/icons';
import { useCallback } from 'react';
import { MenuItem, toast } from '../../..';
import type { CommonMenuItemProps } from './types';
export const CopyLink = ({ onItemClick, onSelect }: CommonMenuItemProps) => {
const t = useAFFiNEI18N();
const copyUrl = useCallback(() => {
navigator.clipboard
.writeText(window.location.href)
.then(() => {
toast(t['Copied link to clipboard']());
})
.catch(err => {
// TODO add error toast here
console.error(err);
});
}, [t]);
return (
<MenuItem
data-testid="copy-link"
onClick={() => {
copyUrl();
onItemClick?.();
onSelect?.();
}}
icon={<CopyIcon />}
>
{t['Copy Link']()}
</MenuItem>
);
};

View File

@ -1,48 +1,27 @@
import { useAFFiNEI18N } from '@affine/i18n/hooks'; import { useAFFiNEI18N } from '@affine/i18n/hooks';
import { ShareIcon } from '@blocksuite/icons'; import { ShareIcon } from '@blocksuite/icons';
import {
MenuIcon,
MenuItem,
type MenuItemProps,
} from '@toeverything/components/menu';
import { MenuItem, styled } from '../../../';
import { PublicLinkDisableModal } from '../../share-menu'; import { PublicLinkDisableModal } from '../../share-menu';
import type { CommonMenuItemProps } from './types';
const StyledMenuItem = styled(MenuItem)(({ theme }) => { export const DisablePublicSharing = (props: MenuItemProps) => {
return {
div: {
color: theme.palette.error.main,
svg: {
color: theme.palette.error.main,
},
},
':hover': {
div: {
color: theme.palette.error.main,
svg: {
color: theme.palette.error.main,
},
},
},
};
});
export const DisablePublicSharing = ({
onSelect,
onItemClick,
...props
}: CommonMenuItemProps) => {
const t = useAFFiNEI18N(); const t = useAFFiNEI18N();
return ( return (
<> <MenuItem
<StyledMenuItem type="danger"
{...props} preFix={
onClick={() => { <MenuIcon>
onItemClick?.(); <ShareIcon />
onSelect?.(); </MenuIcon>
}} }
style={{ color: 'red' }} {...props}
icon={<ShareIcon />} >
> {t['Disable Public Sharing']()}
{t['Disable Public Sharing']()} </MenuItem>
</StyledMenuItem>
</>
); );
}; };

View File

@ -2,36 +2,22 @@ import { pushNotificationAtom } from '@affine/component/notification-center';
import { useAFFiNEI18N } from '@affine/i18n/hooks'; import { useAFFiNEI18N } from '@affine/i18n/hooks';
import type { PageBlockModel } from '@blocksuite/blocks'; import type { PageBlockModel } from '@blocksuite/blocks';
import { import {
ArrowRightSmallIcon,
ExportIcon, ExportIcon,
ExportToHtmlIcon, ExportToHtmlIcon,
ExportToMarkdownIcon, ExportToMarkdownIcon,
ExportToPdfIcon, ExportToPdfIcon,
ExportToPngIcon, ExportToPngIcon,
} from '@blocksuite/icons'; } from '@blocksuite/icons';
import { MenuIcon, MenuItem, MenuSub } from '@toeverything/components/menu';
import { useSetAtom } from 'jotai'; import { useSetAtom } from 'jotai';
import { useCallback } from 'react'; import { useCallback } from 'react';
import { Menu, MenuItem } from '../../..';
import { getContentParser } from './get-content-parser'; import { getContentParser } from './get-content-parser';
import type { CommonMenuItemProps } from './types'; import type { CommonMenuItemProps } from './types';
type ExportMenuItemProps = {
iconSize?: number;
gap?: string;
fontSize?: string;
};
const MenuItemStyle = {
padding: '4px 12px',
};
export const ExportToPdfMenuItem = ({ export const ExportToPdfMenuItem = ({
onSelect, onSelect,
style = MenuItemStyle, }: CommonMenuItemProps<{ type: 'pdf' }>) => {
iconSize,
gap,
fontSize,
}: CommonMenuItemProps<{ type: 'pdf' }> & ExportMenuItemProps) => {
const t = useAFFiNEI18N(); const t = useAFFiNEI18N();
const { currentEditor } = globalThis; const { currentEditor } = globalThis;
const setPushNotification = useSetAtom(pushNotificationAtom); const setPushNotification = useSetAtom(pushNotificationAtom);
@ -89,12 +75,12 @@ export const ExportToPdfMenuItem = ({
return ( return (
<MenuItem <MenuItem
data-testid="export-to-pdf" data-testid="export-to-pdf"
onClick={onClickDownloadPDF} onSelect={onClickDownloadPDF}
icon={<ExportToPdfIcon />} preFix={
style={style} <MenuIcon>
iconSize={iconSize} <ExportToPdfIcon />
gap={gap} </MenuIcon>
fontSize={fontSize} }
> >
{t['Export to PDF']()} {t['Export to PDF']()}
</MenuItem> </MenuItem>
@ -103,11 +89,7 @@ export const ExportToPdfMenuItem = ({
export const ExportToHtmlMenuItem = ({ export const ExportToHtmlMenuItem = ({
onSelect, onSelect,
style = MenuItemStyle, }: CommonMenuItemProps<{ type: 'html' }>) => {
iconSize,
gap,
fontSize,
}: CommonMenuItemProps<{ type: 'html' }> & ExportMenuItemProps) => {
const t = useAFFiNEI18N(); const t = useAFFiNEI18N();
const { currentEditor } = globalThis; const { currentEditor } = globalThis;
const setPushNotification = useSetAtom(pushNotificationAtom); const setPushNotification = useSetAtom(pushNotificationAtom);
@ -140,12 +122,12 @@ export const ExportToHtmlMenuItem = ({
<> <>
<MenuItem <MenuItem
data-testid="export-to-html" data-testid="export-to-html"
onClick={onClickExportHtml} onSelect={onClickExportHtml}
icon={<ExportToHtmlIcon />} preFix={
style={style} <MenuIcon>
iconSize={iconSize} <ExportToHtmlIcon />
gap={gap} </MenuIcon>
fontSize={fontSize} }
> >
{t['Export to HTML']()} {t['Export to HTML']()}
</MenuItem> </MenuItem>
@ -155,11 +137,7 @@ export const ExportToHtmlMenuItem = ({
export const ExportToPngMenuItem = ({ export const ExportToPngMenuItem = ({
onSelect, onSelect,
style = MenuItemStyle, }: CommonMenuItemProps<{ type: 'png' }>) => {
iconSize,
gap,
fontSize,
}: CommonMenuItemProps<{ type: 'png' }> & ExportMenuItemProps) => {
const t = useAFFiNEI18N(); const t = useAFFiNEI18N();
const { currentEditor } = globalThis; const { currentEditor } = globalThis;
const setPushNotification = useSetAtom(pushNotificationAtom); const setPushNotification = useSetAtom(pushNotificationAtom);
@ -194,12 +172,12 @@ export const ExportToPngMenuItem = ({
<> <>
<MenuItem <MenuItem
data-testid="export-to-png" data-testid="export-to-png"
onClick={onClickDownloadPNG} onSelect={onClickDownloadPNG}
icon={<ExportToPngIcon />} preFix={
style={style} <MenuIcon>
iconSize={iconSize} <ExportToPngIcon />
gap={gap} </MenuIcon>
fontSize={fontSize} }
> >
{t['Export to PNG']()} {t['Export to PNG']()}
</MenuItem> </MenuItem>
@ -209,11 +187,7 @@ export const ExportToPngMenuItem = ({
export const ExportToMarkdownMenuItem = ({ export const ExportToMarkdownMenuItem = ({
onSelect, onSelect,
style = MenuItemStyle, }: CommonMenuItemProps<{ type: 'markdown' }>) => {
iconSize,
gap,
fontSize,
}: CommonMenuItemProps<{ type: 'markdown' }> & ExportMenuItemProps) => {
const t = useAFFiNEI18N(); const t = useAFFiNEI18N();
const { currentEditor } = globalThis; const { currentEditor } = globalThis;
const setPushNotification = useSetAtom(pushNotificationAtom); const setPushNotification = useSetAtom(pushNotificationAtom);
@ -246,12 +220,12 @@ export const ExportToMarkdownMenuItem = ({
<> <>
<MenuItem <MenuItem
data-testid="export-to-markdown" data-testid="export-to-markdown"
onClick={onClickExportMarkdown} onSelect={onClickExportMarkdown}
icon={<ExportToMarkdownIcon />} preFix={
style={style} <MenuIcon>
iconSize={iconSize} <ExportToMarkdownIcon />
gap={gap} </MenuIcon>
fontSize={fontSize} }
> >
{t['Export to Markdown']()} {t['Export to Markdown']()}
</MenuItem> </MenuItem>
@ -259,16 +233,12 @@ export const ExportToMarkdownMenuItem = ({
); );
}; };
export const Export = ({ // fixme: refactor this file, export function may should be passed by 'props', this file is just a ui component
onItemClick, export const Export = () => {
}: CommonMenuItemProps<{ type: 'markdown' | 'html' | 'pdf' | 'png' }>) => {
const t = useAFFiNEI18N(); const t = useAFFiNEI18N();
return ( return (
<Menu <MenuSub
width={248} items={
trigger="hover"
placement="right-start"
content={
<> <>
<ExportToPdfMenuItem></ExportToPdfMenuItem> <ExportToPdfMenuItem></ExportToPdfMenuItem>
<ExportToHtmlMenuItem></ExportToHtmlMenuItem> <ExportToHtmlMenuItem></ExportToHtmlMenuItem>
@ -276,24 +246,16 @@ export const Export = ({
<ExportToMarkdownMenuItem></ExportToMarkdownMenuItem> <ExportToMarkdownMenuItem></ExportToMarkdownMenuItem>
</> </>
} }
menuStyles={{ triggerOptions={{
borderRadius: '8px', preFix: (
padding: '8px', <MenuIcon>
background: 'var(--affine-background-overlay-panel-color)', <ExportIcon />
</MenuIcon>
),
['data-testid' as string]: 'export-menu',
}} }}
> >
<MenuItem {t.Export()}
data-testid="export-menu" </MenuSub>
icon={<ExportIcon />}
endIcon={<ArrowRightSmallIcon />}
style={MenuItemStyle}
onClick={e => {
e.stopPropagation();
onItemClick?.();
}}
>
{t.Export()}
</MenuItem>
</Menu>
); );
}; };

View File

@ -1,4 +1,3 @@
export * from './copy-link';
export * from './disable-public-sharing'; export * from './disable-public-sharing';
export * from './export'; export * from './export';
// export * from './MoveTo'; // export * from './MoveTo';

View File

@ -1,31 +1,28 @@
import { useAFFiNEI18N } from '@affine/i18n/hooks'; import { useAFFiNEI18N } from '@affine/i18n/hooks';
import { DeleteTemporarilyIcon } from '@blocksuite/icons'; import { DeleteTemporarilyIcon } from '@blocksuite/icons';
import {
MenuIcon,
MenuItem,
type MenuItemProps,
} from '@toeverything/components/menu';
import type { ConfirmProps } from '../../..'; import type { ConfirmProps } from '../../..';
import { Confirm, MenuItem } from '../../..'; import { Confirm } from '../../..';
import { moveToTrashStyle } from './index.css'; export const MoveToTrash = (props: MenuItemProps) => {
import type { CommonMenuItemProps } from './types';
export const MoveToTrash = ({
onSelect,
onItemClick,
...props
}: CommonMenuItemProps) => {
const t = useAFFiNEI18N(); const t = useAFFiNEI18N();
return ( return (
<> <MenuItem
<MenuItem preFix={
{...props} <MenuIcon>
onClick={() => { <DeleteTemporarilyIcon />
onItemClick?.(); </MenuIcon>
onSelect?.(); }
}} type="danger"
icon={<DeleteTemporarilyIcon />} {...props}
className={moveToTrashStyle} >
> {t['Move to Trash']()}
{t['Move to Trash']()} </MenuItem>
</MenuItem>
</>
); );
}; };

View File

@ -6,13 +6,12 @@ import type { GetPageInfoById } from '@affine/env/page-info';
import { useAFFiNEI18N } from '@affine/i18n/hooks'; import { useAFFiNEI18N } from '@affine/i18n/hooks';
import { FilteredIcon, FolderIcon, ViewLayersIcon } from '@blocksuite/icons'; import { FilteredIcon, FolderIcon, ViewLayersIcon } from '@blocksuite/icons';
import { Button } from '@toeverything/components/button'; import { Button } from '@toeverything/components/button';
import { Menu, MenuIcon, MenuItem } from '@toeverything/components/menu';
import { Tooltip } from '@toeverything/components/tooltip'; import { Tooltip } from '@toeverything/components/tooltip';
import clsx from 'clsx'; import clsx from 'clsx';
import type { MouseEvent } from 'react'; import type { MouseEvent } from 'react';
import { useCallback, useState } from 'react'; import { useCallback, useRef, useState } from 'react';
import { MenuItem } from '../../..';
import Menu from '../../../ui/menu/menu';
import { CreateFilterMenu } from '../filter/vars'; import { CreateFilterMenu } from '../filter/vars';
import type { useCollectionManager } from '../use-collection-manager'; import type { useCollectionManager } from '../use-collection-manager';
import * as styles from './collection-list.css'; import * as styles from './collection-list.css';
@ -40,7 +39,11 @@ const CollectionOption = ({
return ( return (
<MenuItem <MenuItem
data-testid="collection-select-option" data-testid="collection-select-option"
icon={<ViewLayersIcon></ViewLayersIcon>} preFix={
<MenuIcon>
<ViewLayersIcon />
</MenuIcon>
}
onClick={selectCollection} onClick={selectCollection}
key={collection.id} key={collection.id}
className={styles.viewMenu} className={styles.viewMenu}
@ -107,6 +110,7 @@ export const CollectionList = ({
getPageInfo: GetPageInfoById; getPageInfo: GetPageInfoById;
propertiesMeta: PropertiesMeta; propertiesMeta: PropertiesMeta;
}) => { }) => {
const ref = useRef(null);
const t = useAFFiNEI18N(); const t = useAFFiNEI18N();
const [collection, setCollection] = useState<Collection>(); const [collection, setCollection] = useState<Collection>();
const onChange = useCallback( const onChange = useCallback(
@ -134,14 +138,20 @@ export const CollectionList = ({
[closeUpdateCollectionModal, setting] [closeUpdateCollectionModal, setting]
); );
return ( return (
<FlexWrapper alignItems="center"> <FlexWrapper alignItems="center" ref={ref}>
{setting.savedCollections.length > 0 && ( {setting.savedCollections.length > 0 && (
<Menu <Menu
trigger="click" portalOptions={{
content={ container: ref.current,
}}
items={
<div style={{ minWidth: 150 }}> <div style={{ minWidth: 150 }}>
<MenuItem <MenuItem
icon={<FolderIcon></FolderIcon>} preFix={
<MenuIcon>
<FolderIcon />
</MenuIcon>
}
onClick={setting.backToAll} onClick={setting.backToAll}
className={styles.viewMenu} className={styles.viewMenu}
> >
@ -184,17 +194,22 @@ export const CollectionList = ({
</Menu> </Menu>
)} )}
<Menu <Menu
trigger="click" items={
placement="bottom-start"
content={
<CreateFilterMenu <CreateFilterMenu
propertiesMeta={propertiesMeta} propertiesMeta={propertiesMeta}
value={setting.currentCollection.filterList} value={setting.currentCollection.filterList}
onChange={onChange} onChange={onChange}
/> />
} }
portalOptions={{
container: ref.current,
}}
> >
<Button icon={<FilteredIcon />} data-testid="create-first-filter"> <Button
type="default"
icon={<FilteredIcon />}
data-testid="create-first-filter"
>
{t['com.affine.filter']()} {t['com.affine.filter']()}
</Button> </Button>
</Menu> </Menu>

View File

@ -17,26 +17,10 @@ export const ShareExport = () => {
{t['com.affine.share-menu.ShareViaExport']()} {t['com.affine.share-menu.ShareViaExport']()}
</div> </div>
<div> <div>
<ExportToPdfMenuItem <ExportToPdfMenuItem />
style={{ padding: '4px' }} <ExportToHtmlMenuItem />
iconSize={16} <ExportToPngMenuItem />
gap={'4px'} <ExportToMarkdownMenuItem />
/>
<ExportToHtmlMenuItem
style={{ padding: '4px' }}
iconSize={16}
gap={'4px'}
/>
<ExportToPngMenuItem
style={{ padding: '4px' }}
iconSize={16}
gap={'4px'}
/>
<ExportToMarkdownMenuItem
style={{ padding: '4px' }}
iconSize={16}
gap={'4px'}
/>
</div> </div>
<div className={styles.columnContainerStyle}> <div className={styles.columnContainerStyle}>
<div className={styles.descriptionStyle}> <div className={styles.descriptionStyle}>

View File

@ -8,12 +8,10 @@ import { useAFFiNEI18N } from '@affine/i18n/hooks';
import type { Page } from '@blocksuite/store'; import type { Page } from '@blocksuite/store';
import { Button } from '@toeverything/components/button'; import { Button } from '@toeverything/components/button';
import { Divider } from '@toeverything/components/divider'; import { Divider } from '@toeverything/components/divider';
import { useAtom } from 'jotai'; import { Menu } from '@toeverything/components/menu';
import { useCallback } from 'react'; import { useRef } from 'react';
import { Menu } from '../../ui/menu/menu';
import * as styles from './index.css'; import * as styles from './index.css';
import { enableShareMenuAtom } from './index.jotai';
import { ShareExport } from './share-export'; import { ShareExport } from './share-export';
import { SharePage } from './share-page'; import { SharePage } from './share-page';
@ -35,11 +33,11 @@ export interface ShareMenuProps<
export const ShareMenu = (props: ShareMenuProps) => { export const ShareMenu = (props: ShareMenuProps) => {
const { useIsSharedPage } = props; const { useIsSharedPage } = props;
const ref = useRef(null);
const isSharedPage = useIsSharedPage( const isSharedPage = useIsSharedPage(
props.workspace.id, props.workspace.id,
props.currentPage.id props.currentPage.id
); );
const [open, setOpen] = useAtom(enableShareMenuAtom);
const t = useAFFiNEI18N(); const t = useAFFiNEI18N();
const content = ( const content = (
<div className={styles.containerStyle}> <div className={styles.containerStyle}>
@ -52,28 +50,12 @@ export const ShareMenu = (props: ShareMenuProps) => {
); );
return ( return (
<Menu <Menu
menuStyles={{ items={content}
padding: '12px', portalOptions={{
background: 'var(--affine-background-overlay-panel-color)', container: ref.current,
transform: 'translateX(-10px)',
}} }}
content={content}
visible={open}
placement="bottom"
trigger={['click']}
width={410}
disablePortal={true}
onClickAway={useCallback(() => {
setOpen(false);
}, [setOpen])}
> >
<Button <Button data-testid="share-menu-button" type="plain" ref={ref}>
data-testid="share-menu-button"
onClick={useCallback(() => {
setOpen(value => !value);
}, [setOpen])}
type={'plain'}
>
<div <div
style={{ style={{
color: isSharedPage color: isSharedPage

View File

@ -1,15 +1,9 @@
import { import { RadioButton, RadioButtonGroup, Switch } from '@affine/component';
Menu,
MenuItem,
MenuTrigger,
RadioButton,
RadioButtonGroup,
Switch,
} from '@affine/component';
import { WorkspaceFlavour } from '@affine/env/workspace'; import { WorkspaceFlavour } from '@affine/env/workspace';
import { useAFFiNEI18N } from '@affine/i18n/hooks'; import { useAFFiNEI18N } from '@affine/i18n/hooks';
import { ArrowRightSmallIcon, WebIcon } from '@blocksuite/icons'; import { ArrowRightSmallIcon, WebIcon } from '@blocksuite/icons';
import { Button } from '@toeverything/components/button'; import { Button } from '@toeverything/components/button';
import { Menu, MenuItem, MenuTrigger } from '@toeverything/components/menu';
import { useState } from 'react'; import { useState } from 'react';
import { useCallback, useMemo } from 'react'; import { useCallback, useMemo } from 'react';
@ -186,20 +180,8 @@ export const AffineSharePage = (props: ShareMenuProps) => {
<div className={styles.rowContainerStyle}> <div className={styles.rowContainerStyle}>
<div className={styles.subTitleStyle}>Link expires</div> <div className={styles.subTitleStyle}>Link expires</div>
<div> <div>
<Menu <Menu items={<MenuItem>Never</MenuItem>}>
content={<MenuItem>Never</MenuItem>} <MenuTrigger>Never</MenuTrigger>
placement="bottom-end"
trigger="click"
>
<MenuTrigger
style={{
display: 'flex',
justifyContent: 'space-between',
padding: '4px 6px 4px 10px',
}}
>
Never
</MenuTrigger>
</Menu> </Menu>
</div> </div>
</div> </div>

View File

@ -1,5 +1,6 @@
export * from './menu'; /**
// export { StyledMenuItem as MenuItem } from './styles'; * @deprecated
* Use @toeverything/components/menu instead, this component only used in bookmark plugin, since it support set anchor as Range
*/
export * from './menu-item'; export * from './menu-item';
export * from './menu-trigger';
export * from './pure-menu'; export * from './pure-menu';

View File

@ -1,20 +0,0 @@
import { ArrowDownSmallIcon } from '@blocksuite/icons';
import { Button, type ButtonProps } from '@toeverything/components/button';
import { forwardRef } from 'react';
export const MenuTrigger = forwardRef<HTMLButtonElement, ButtonProps>(
({ children, ...props }, ref) => {
return (
<Button
// type="plain"
ref={ref}
icon={<ArrowDownSmallIcon />}
iconPosition="end"
{...props}
>
{children}
</Button>
);
}
);
MenuTrigger.displayName = 'MenuTrigger';

View File

@ -1,41 +0,0 @@
import type { TooltipProps } from '@mui/material';
import type { CSSProperties } from 'react';
import { Popper, type PopperProps } from '../popper';
import { StyledMenuWrapper } from './styles';
export type MenuProps = {
width?: CSSProperties['width'];
menuStyles?: CSSProperties;
} & PopperProps &
Omit<TooltipProps, 'title' | 'content' | 'placement'>;
export const Menu = (props: MenuProps) => {
const {
width,
menuStyles,
content,
placement = 'bottom-start',
children,
...popperProps
} = props;
return content ? (
<Popper
placement={placement}
{...popperProps}
showArrow={false}
content={
<StyledMenuWrapper
width={width}
placement={placement}
style={menuStyles}
>
{content}
</StyledMenuWrapper>
}
>
{children}
</Popper>
) : null;
};
export default Menu;

View File

@ -20,7 +20,7 @@
"dependencies": { "dependencies": {
"@affine/component": "workspace:*", "@affine/component": "workspace:*",
"@affine/sdk": "workspace:*", "@affine/sdk": "workspace:*",
"@blocksuite/icons": "^2.1.31", "@blocksuite/icons": "^2.1.32",
"foxact": "^0.2.20", "foxact": "^0.2.20",
"link-preview-js": "^3.0.5" "link-preview-js": "^3.0.5"
}, },

View File

@ -16,7 +16,7 @@
"dependencies": { "dependencies": {
"@affine/component": "workspace:*", "@affine/component": "workspace:*",
"@affine/sdk": "workspace:*", "@affine/sdk": "workspace:*",
"@toeverything/components": "^0.0.25", "@toeverything/components": "^0.0.31",
"idb": "^7.1.1", "idb": "^7.1.1",
"langchain": "^0.0.138", "langchain": "^0.0.138",
"marked": "^7.0.5", "marked": "^7.0.5",

View File

@ -17,8 +17,8 @@
"dependencies": { "dependencies": {
"@affine/component": "workspace:*", "@affine/component": "workspace:*",
"@affine/sdk": "workspace:*", "@affine/sdk": "workspace:*",
"@blocksuite/icons": "^2.1.31", "@blocksuite/icons": "^2.1.32",
"@toeverything/components": "^0.0.25" "@toeverything/components": "^0.0.31"
}, },
"devDependencies": { "devDependencies": {
"@affine/plugin-cli": "workspace:*" "@affine/plugin-cli": "workspace:*"

View File

@ -17,7 +17,7 @@
"@affine/component": "workspace:*", "@affine/component": "workspace:*",
"@affine/sdk": "workspace:*", "@affine/sdk": "workspace:*",
"@blocksuite/icons": "^2.1.31", "@blocksuite/icons": "^2.1.31",
"@toeverything/components": "^0.0.25", "@toeverything/components": "^0.0.31",
"@toeverything/theme": "^0.7.15", "@toeverything/theme": "^0.7.15",
"clsx": "^2.0.0", "clsx": "^2.0.0",
"foxact": "^0.2.20", "foxact": "^0.2.20",

View File

@ -17,8 +17,8 @@
"dependencies": { "dependencies": {
"@affine/component": "workspace:*", "@affine/component": "workspace:*",
"@affine/sdk": "workspace:*", "@affine/sdk": "workspace:*",
"@blocksuite/icons": "^2.1.31", "@blocksuite/icons": "^2.1.32",
"@toeverything/components": "^0.0.25" "@toeverything/components": "^0.0.31"
}, },
"devDependencies": { "devDependencies": {
"@affine/plugin-cli": "workspace:*", "@affine/plugin-cli": "workspace:*",

View File

@ -29,7 +29,7 @@ function getAllPage(page: Page) {
.locator('table') .locator('table')
.getByRole('button', { name: 'New Page' }); .getByRole('button', { name: 'New Page' });
const newPageDropdown = newPageButton.locator('svg'); const newPageDropdown = newPageButton.locator('svg');
const edgelessBlockCard = page.locator('table').getByText('New Edgeless'); const edgelessBlockCard = page.getByTestId('new-edgeless-button-in-all-page');
async function clickNewPageButton() { async function clickNewPageButton() {
return newPageButton.click(); return newPageButton.click();
@ -106,11 +106,8 @@ test('allow creation of filters by created time', async ({ page }) => {
await fillDatePicker(page, today); await fillDatePicker(page, today);
await checkPagesCount(page, 0); await checkPagesCount(page, 0);
// change filter // change filter
await page.locator('[data-testid="filter-name"]').click(); await page.getByTestId('filter-name').click();
await page await page.getByTestId('filler-tag-before').click();
.locator('[data-testid="filter-name-select"]')
.locator('button', { hasText: 'before' })
.click();
const tomorrow = new Date(); const tomorrow = new Date();
tomorrow.setDate(tomorrow.getDate() + 1); tomorrow.setDate(tomorrow.getDate() + 1);
await fillDatePicker(page, tomorrow); await fillDatePicker(page, tomorrow);
@ -142,10 +139,7 @@ test('creation of filters by created time, then click date picker to modify the
await checkPagesCount(page, 0); await checkPagesCount(page, 0);
// change filter // change filter
await page.locator('[data-testid="filter-name"]').click(); await page.locator('[data-testid="filter-name"]').click();
await page await page.getByTestId('filler-tag-before').click();
.locator('[data-testid="filter-name-select"]')
.locator('button', { hasText: 'before' })
.click();
const tomorrow = new Date(); const tomorrow = new Date();
tomorrow.setDate(tomorrow.getDate() + 1); tomorrow.setDate(tomorrow.getDate() + 1);
await selectDateFromDatePicker(page, tomorrow); await selectDateFromDatePicker(page, tomorrow);
@ -196,11 +190,11 @@ test('allow creation of filters by tags', async ({ page }) => {
await createFirstFilter(page, 'Tags'); await createFirstFilter(page, 'Tags');
await checkFilterName(page, 'is not empty'); await checkFilterName(page, 'is not empty');
await checkPagesCount(page, pagesWithTagsCount + 2); await checkPagesCount(page, pagesWithTagsCount + 2);
await changeFilter(page, /^contains all/); await changeFilter(page, 'contains all');
await checkPagesCount(page, pageCount + 2); await checkPagesCount(page, pageCount + 2);
await selectTag(page, 'A'); await selectTag(page, 'A');
await checkPagesCount(page, 1); await checkPagesCount(page, 1);
await changeFilter(page, /^does not contains all/); await changeFilter(page, 'does not contains all');
await selectTag(page, 'B'); await selectTag(page, 'B');
await checkPagesCount(page, pageCount + 1); await checkPagesCount(page, pageCount + 1);
}); });

View File

@ -29,10 +29,7 @@ const createAndPinCollection = async (
}); });
await expect(cell).toBeVisible(); await expect(cell).toBeVisible();
await page.getByTestId('create-first-filter').click(); await page.getByTestId('create-first-filter').click();
await page await page.getByTestId(`filler-tag-Created`).click();
.getByTestId('variable-select')
.locator('button', { hasText: 'Created' })
.click();
await page.getByTestId('save-as-collection').click(); await page.getByTestId('save-as-collection').click();
const title = page.getByTestId('input-collection-title'); const title = page.getByTestId('input-collection-title');
await title.isVisible(); await title.isVisible();
@ -156,7 +153,8 @@ test('create temporary filter by click tag', async ({ page }) => {
await expect(cell).toBeVisible(); await expect(cell).toBeVisible();
expect(await page.getByTestId('title').count()).toBe(1); expect(await page.getByTestId('title').count()).toBe(1);
await page.getByTestId('filter-arg').click(); await page.getByTestId('filter-arg').click();
await page.getByRole('tooltip').getByText('TODO Tag').click();
await page.getByTestId('multi-select-TODO Tag').click();
expect(await page.getByTestId('title').count()).toBeGreaterThanOrEqual(2); expect(await page.getByTestId('title').count()).toBeGreaterThanOrEqual(2);
}); });

View File

@ -38,7 +38,7 @@ test('Export to html, markdown and png', async ({ page }) => {
await waitForEditorLoad(page); await waitForEditorLoad(page);
{ {
await clickPageMoreActions(page); await clickPageMoreActions(page);
await page.getByTestId('export-menu').click(); await page.getByTestId('export-menu').hover();
const downloadPromise = page.waitForEvent('download'); const downloadPromise = page.waitForEvent('download');
await page.getByTestId('export-to-markdown').click(); await page.getByTestId('export-to-markdown').click();
await downloadPromise; await downloadPromise;

View File

@ -25,7 +25,7 @@ test('click btn bew page and open in tab', async ({ page, workspace }) => {
.click(); .click();
const [newTabPage] = await Promise.all([ const [newTabPage] = await Promise.all([
page.waitForEvent('popup'), page.waitForEvent('popup'),
page.getByRole('button', { name: 'Open in new tab' }).click(), page.getByRole('menuitem', { name: 'Open in new tab' }).click(),
]); ]);
expect(newTabPage.url()).toBe(newPageUrl); expect(newTabPage.url()).toBe(newPageUrl);

View File

@ -39,18 +39,6 @@ test('change language using keyboard', async ({ page }) => {
const newName = await locator.textContent(); const newName = await locator.textContent();
expect(oldName).not.toBe(newName); expect(oldName).not.toBe(newName);
} }
await locator.click();
await page.waitForTimeout(200);
await page.keyboard.press('ArrowUp', {
delay: 50,
});
await page.keyboard.press('Enter', {
delay: 50,
});
{
const newName = await locator.textContent();
expect(oldName).toBe(newName);
}
}); });
test('Change theme', async ({ page }) => { test('Change theme', async ({ page }) => {

View File

@ -200,20 +200,12 @@ export const createPageWithTag = async (
await page.keyboard.press('Escape'); await page.keyboard.press('Escape');
}; };
export const changeFilter = async (page: Page, to: string | RegExp) => { export const changeFilter = async (page: Page, to: string) => {
await page.getByTestId('filter-name').click(); await page.getByTestId('filter-name').click();
await page await page.getByTestId(`filler-tag-${to}`).click();
.getByTestId('filter-name-select')
.locator('button', { hasText: to })
.click();
}; };
export async function selectTag(page: Page, name: string | RegExp) { export async function selectTag(page: Page, name: string | RegExp) {
await page.getByTestId('filter-arg').click(); await page.getByTestId('filter-arg').click();
await page await page.getByTestId(`multi-select-${name}`).click();
.getByTestId('multi-select')
.getByTestId('select-option')
.getByText(name, { exact: true })
.click();
await page.getByTestId('filter-arg').click();
} }

View File

@ -121,7 +121,7 @@ __metadata:
"@affine/component": "workspace:*" "@affine/component": "workspace:*"
"@affine/plugin-cli": "workspace:*" "@affine/plugin-cli": "workspace:*"
"@affine/sdk": "workspace:*" "@affine/sdk": "workspace:*"
"@blocksuite/icons": ^2.1.31 "@blocksuite/icons": ^2.1.32
foxact: ^0.2.20 foxact: ^0.2.20
link-preview-js: ^3.0.5 link-preview-js: ^3.0.5
languageName: unknown languageName: unknown
@ -154,7 +154,7 @@ __metadata:
"@blocksuite/blocks": 0.0.0-20230829150056-df43987c-nightly "@blocksuite/blocks": 0.0.0-20230829150056-df43987c-nightly
"@blocksuite/editor": 0.0.0-20230829150056-df43987c-nightly "@blocksuite/editor": 0.0.0-20230829150056-df43987c-nightly
"@blocksuite/global": 0.0.0-20230829150056-df43987c-nightly "@blocksuite/global": 0.0.0-20230829150056-df43987c-nightly
"@blocksuite/icons": ^2.1.31 "@blocksuite/icons": ^2.1.32
"@blocksuite/lit": 0.0.0-20230829150056-df43987c-nightly "@blocksuite/lit": 0.0.0-20230829150056-df43987c-nightly
"@blocksuite/store": 0.0.0-20230829150056-df43987c-nightly "@blocksuite/store": 0.0.0-20230829150056-df43987c-nightly
"@dnd-kit/core": ^6.0.8 "@dnd-kit/core": ^6.0.8
@ -212,7 +212,7 @@ __metadata:
"@affine/component": "workspace:*" "@affine/component": "workspace:*"
"@affine/plugin-cli": "workspace:*" "@affine/plugin-cli": "workspace:*"
"@affine/sdk": "workspace:*" "@affine/sdk": "workspace:*"
"@toeverything/components": ^0.0.25 "@toeverything/components": ^0.0.31
"@types/marked": ^5.0.1 "@types/marked": ^5.0.1
idb: ^7.1.1 idb: ^7.1.1
jotai: ^2.4.1 jotai: ^2.4.1
@ -247,7 +247,7 @@ __metadata:
"@blocksuite/blocks": 0.0.0-20230829150056-df43987c-nightly "@blocksuite/blocks": 0.0.0-20230829150056-df43987c-nightly
"@blocksuite/editor": 0.0.0-20230829150056-df43987c-nightly "@blocksuite/editor": 0.0.0-20230829150056-df43987c-nightly
"@blocksuite/global": 0.0.0-20230829150056-df43987c-nightly "@blocksuite/global": 0.0.0-20230829150056-df43987c-nightly
"@blocksuite/icons": ^2.1.31 "@blocksuite/icons": ^2.1.32
"@blocksuite/lit": 0.0.0-20230829150056-df43987c-nightly "@blocksuite/lit": 0.0.0-20230829150056-df43987c-nightly
"@blocksuite/store": 0.0.0-20230829150056-df43987c-nightly "@blocksuite/store": 0.0.0-20230829150056-df43987c-nightly
"@dnd-kit/core": ^6.0.8 "@dnd-kit/core": ^6.0.8
@ -264,7 +264,7 @@ __metadata:
"@sentry/webpack-plugin": ^2.7.0 "@sentry/webpack-plugin": ^2.7.0
"@svgr/webpack": ^8.1.0 "@svgr/webpack": ^8.1.0
"@swc/core": ^1.3.81 "@swc/core": ^1.3.81
"@toeverything/components": ^0.0.25 "@toeverything/components": ^0.0.31
"@types/lodash-es": ^4.17.9 "@types/lodash-es": ^4.17.9
"@types/webpack-env": ^1.18.1 "@types/webpack-env": ^1.18.1
async-call-rpc: ^6.3.1 async-call-rpc: ^6.3.1
@ -439,8 +439,8 @@ __metadata:
"@affine/component": "workspace:*" "@affine/component": "workspace:*"
"@affine/plugin-cli": "workspace:*" "@affine/plugin-cli": "workspace:*"
"@affine/sdk": "workspace:*" "@affine/sdk": "workspace:*"
"@blocksuite/icons": ^2.1.31 "@blocksuite/icons": ^2.1.32
"@toeverything/components": ^0.0.25 "@toeverything/components": ^0.0.31
languageName: unknown languageName: unknown
linkType: soft linkType: soft
@ -466,7 +466,7 @@ __metadata:
"@affine/plugin-cli": "workspace:*" "@affine/plugin-cli": "workspace:*"
"@affine/sdk": "workspace:*" "@affine/sdk": "workspace:*"
"@blocksuite/icons": ^2.1.31 "@blocksuite/icons": ^2.1.31
"@toeverything/components": ^0.0.25 "@toeverything/components": ^0.0.31
"@toeverything/theme": ^0.7.15 "@toeverything/theme": ^0.7.15
clsx: ^2.0.0 clsx: ^2.0.0
foxact: ^0.2.20 foxact: ^0.2.20
@ -584,8 +584,8 @@ __metadata:
"@affine/component": "workspace:*" "@affine/component": "workspace:*"
"@affine/plugin-cli": "workspace:*" "@affine/plugin-cli": "workspace:*"
"@affine/sdk": "workspace:*" "@affine/sdk": "workspace:*"
"@blocksuite/icons": ^2.1.31 "@blocksuite/icons": ^2.1.32
"@toeverything/components": ^0.0.25 "@toeverything/components": ^0.0.31
jotai: ^2.4.1 jotai: ^2.4.1
react: 18.2.0 react: 18.2.0
react-dom: 18.2.0 react-dom: 18.2.0
@ -626,7 +626,7 @@ __metadata:
"@blocksuite/blocks": 0.0.0-20230829150056-df43987c-nightly "@blocksuite/blocks": 0.0.0-20230829150056-df43987c-nightly
"@blocksuite/editor": 0.0.0-20230829150056-df43987c-nightly "@blocksuite/editor": 0.0.0-20230829150056-df43987c-nightly
"@blocksuite/global": 0.0.0-20230829150056-df43987c-nightly "@blocksuite/global": 0.0.0-20230829150056-df43987c-nightly
"@blocksuite/icons": ^2.1.31 "@blocksuite/icons": ^2.1.32
"@blocksuite/lit": 0.0.0-20230829150056-df43987c-nightly "@blocksuite/lit": 0.0.0-20230829150056-df43987c-nightly
"@blocksuite/store": 0.0.0-20230829150056-df43987c-nightly "@blocksuite/store": 0.0.0-20230829150056-df43987c-nightly
"@toeverything/hooks": "workspace:*" "@toeverything/hooks": "workspace:*"
@ -762,7 +762,7 @@ __metadata:
"@blocksuite/blocks": 0.0.0-20230829150056-df43987c-nightly "@blocksuite/blocks": 0.0.0-20230829150056-df43987c-nightly
"@blocksuite/editor": 0.0.0-20230829150056-df43987c-nightly "@blocksuite/editor": 0.0.0-20230829150056-df43987c-nightly
"@blocksuite/global": 0.0.0-20230829150056-df43987c-nightly "@blocksuite/global": 0.0.0-20230829150056-df43987c-nightly
"@blocksuite/icons": ^2.1.31 "@blocksuite/icons": ^2.1.32
"@blocksuite/lit": 0.0.0-20230829150056-df43987c-nightly "@blocksuite/lit": 0.0.0-20230829150056-df43987c-nightly
"@blocksuite/store": 0.0.0-20230829150056-df43987c-nightly "@blocksuite/store": 0.0.0-20230829150056-df43987c-nightly
"@storybook/addon-actions": ^7.4.0 "@storybook/addon-actions": ^7.4.0
@ -3525,7 +3525,7 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"@blocksuite/icons@npm:^2.1.30, @blocksuite/icons@npm:^2.1.31": "@blocksuite/icons@npm:^2.1.31":
version: 2.1.31 version: 2.1.31
resolution: "@blocksuite/icons@npm:2.1.31" resolution: "@blocksuite/icons@npm:2.1.31"
peerDependencies: peerDependencies:
@ -3535,6 +3535,26 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"@blocksuite/icons@npm:^2.1.32":
version: 2.1.32
resolution: "@blocksuite/icons@npm:2.1.32"
peerDependencies:
"@types/react": ^18.0.25
react: ^18.2.0
checksum: 76179a20fd67c9a5c7756439b25636bd550509ddc34951243a6a42a7f95cd7427591e16b87a2ebbac09e3fadef1a62e4ec30de31d419872c27f1130cb58f4c38
languageName: node
linkType: hard
"@blocksuite/icons@npm:^2.1.33":
version: 2.1.33
resolution: "@blocksuite/icons@npm:2.1.33"
peerDependencies:
"@types/react": ^18.0.25
react: ^18.2.0
checksum: b29f5ffb19f09937a9053028b1fd70e497f54be82673b91889a2b9d2b23e2f853872415bd638f81dc4917370f555c330573139e4a58d88c588d04a0111f5117b
languageName: node
linkType: hard
"@blocksuite/lit@npm:0.0.0-20230829150056-df43987c-nightly": "@blocksuite/lit@npm:0.0.0-20230829150056-df43987c-nightly":
version: 0.0.0-20230829150056-df43987c-nightly version: 0.0.0-20230829150056-df43987c-nightly
resolution: "@blocksuite/lit@npm:0.0.0-20230829150056-df43987c-nightly" resolution: "@blocksuite/lit@npm:0.0.0-20230829150056-df43987c-nightly"
@ -12451,11 +12471,11 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"@toeverything/components@npm:^0.0.25": "@toeverything/components@npm:^0.0.31":
version: 0.0.25 version: 0.0.31
resolution: "@toeverything/components@npm:0.0.25" resolution: "@toeverything/components@npm:0.0.31"
dependencies: dependencies:
"@blocksuite/icons": ^2.1.30 "@blocksuite/icons": ^2.1.33
"@radix-ui/react-dropdown-menu": ^2.0.5 "@radix-ui/react-dropdown-menu": ^2.0.5
"@radix-ui/react-tooltip": ^1.0.6 "@radix-ui/react-tooltip": ^1.0.6
peerDependencies: peerDependencies:
@ -12463,7 +12483,7 @@ __metadata:
clsx: ^2 clsx: ^2
react: ^18 react: ^18
react-dom: ^18 react-dom: ^18
checksum: e4f5b49c81c9ef78e72ccb5e097d36796793865302f346d64f9eb5016e9ef6e1936047439aa698fde9e52d96ec5a1c5e6f4877bc04c71f69e7091c60c427e981 checksum: a47263e25c9b6f987cc97d75cbf515a41ca7cce26f67c7c2017ad0e1331d5e4e59c50b1b511970fd28210e3437dfb19a83016678c3c633f0110065b6156ce289
languageName: node languageName: node
linkType: hard linkType: hard