mirror of
https://github.com/toeverything/AFFiNE.git
synced 2024-12-23 21:11:51 +03:00
feat: replace menu with new design (#4012)
Co-authored-by: Alex Yang <himself65@outlook.com>
This commit is contained in:
parent
ef3d3a34e2
commit
d8c9f10bc1
@ -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",
|
||||||
|
@ -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>
|
||||||
);
|
);
|
||||||
|
@ -0,0 +1,5 @@
|
|||||||
|
import { style } from '@vanilla-extract/css';
|
||||||
|
|
||||||
|
export const hoveredLanguageItem = style({
|
||||||
|
background: 'var(--affine-hover-color)',
|
||||||
|
});
|
@ -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}
|
||||||
|
@ -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>
|
||||||
|
@ -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 ? (
|
||||||
|
@ -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}
|
||||||
|
@ -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';
|
||||||
|
@ -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)',
|
||||||
|
});
|
@ -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>
|
||||||
|
@ -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)',
|
||||||
|
}
|
||||||
|
: {}),
|
||||||
|
};
|
||||||
|
});
|
||||||
|
@ -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 (
|
||||||
|
@ -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 (
|
||||||
|
@ -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:*",
|
||||||
|
@ -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",
|
||||||
|
@ -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",
|
||||||
|
@ -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',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
@ -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)',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
@ -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';
|
@ -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();
|
||||||
|
@ -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>
|
||||||
);
|
);
|
||||||
|
@ -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}
|
||||||
|
@ -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}
|
||||||
|
@ -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>;
|
||||||
|
};
|
@ -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>
|
||||||
|
@ -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));
|
||||||
|
@ -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>
|
||||||
|
@ -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>
|
|
||||||
);
|
|
||||||
};
|
|
@ -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>
|
|
||||||
</>
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -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>
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -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';
|
||||||
|
@ -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>
|
|
||||||
</>
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -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>
|
||||||
|
@ -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}>
|
||||||
|
@ -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
|
||||||
|
@ -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>
|
||||||
|
@ -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';
|
||||||
|
@ -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';
|
|
@ -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;
|
|
@ -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"
|
||||||
},
|
},
|
||||||
|
@ -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",
|
||||||
|
@ -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:*"
|
||||||
|
@ -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",
|
||||||
|
@ -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:*",
|
||||||
|
@ -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);
|
||||||
});
|
});
|
||||||
|
@ -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);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -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;
|
||||||
|
@ -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);
|
||||||
|
@ -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 }) => {
|
||||||
|
@ -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();
|
|
||||||
}
|
}
|
||||||
|
56
yarn.lock
56
yarn.lock
@ -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
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user