mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-02-13 12:55:00 +00:00
feat: replace menu with new design (#4012)
Co-authored-by: Alex Yang <himself65@outlook.com>
This commit is contained in:
@@ -62,10 +62,10 @@ export const content = style({
|
||||
|
||||
export const postfix = style({
|
||||
justifySelf: 'flex-end',
|
||||
display: 'none',
|
||||
visibility: 'hidden',
|
||||
selectors: {
|
||||
[`${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 { EdgelessIcon, ImportIcon, PageIcon } from '@blocksuite/icons';
|
||||
import { Menu } from '@toeverything/components/menu';
|
||||
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 { DropdownButton } from './dropdown';
|
||||
|
||||
type NewPageButtonProps = {
|
||||
createNewPage: () => void;
|
||||
@@ -32,18 +33,21 @@ export const CreateNewPagePopup = ({
|
||||
desc={t['com.affine.write_with_a_blank_page']()}
|
||||
right={<PageIcon width={20} height={20} />}
|
||||
onClick={createNewPage}
|
||||
data-testid="new-page-button-in-all-page"
|
||||
/>
|
||||
<BlockCard
|
||||
title={t['com.affine.new_edgeless']()}
|
||||
desc={t['com.affine.draw_with_a_blank_whiteboard']()}
|
||||
right={<EdgelessIcon width={20} height={20} />}
|
||||
onClick={createNewEdgeless}
|
||||
data-testid="new-edgeless-button-in-all-page"
|
||||
/>
|
||||
<BlockCard
|
||||
title={t['com.affine.new_import']()}
|
||||
desc={t['com.affine.import_file']()}
|
||||
right={<ImportIcon width={20} height={20} />}
|
||||
onClick={importFile}
|
||||
data-testid="import-button-in-all-page"
|
||||
/>
|
||||
{/* TODO Import */}
|
||||
</div>
|
||||
@@ -59,18 +63,10 @@ export const NewPageButton = ({
|
||||
const [open, setOpen] = useState(false);
|
||||
return (
|
||||
<Menu
|
||||
visible={open}
|
||||
placement="bottom-end"
|
||||
trigger={['click']}
|
||||
disablePortal={true}
|
||||
onClickAway={() => {
|
||||
setOpen(false);
|
||||
rootOptions={{
|
||||
open,
|
||||
}}
|
||||
menuStyles={{
|
||||
padding: '0px',
|
||||
background: 'var(--affine-background-overlay-panel-color)',
|
||||
}}
|
||||
content={
|
||||
items={
|
||||
<CreateNewPagePopup
|
||||
createNewPage={() => {
|
||||
createNewPage();
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
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';
|
||||
|
||||
// fixme: This component should use popover instead of menu
|
||||
export const Tags = ({ value }: { value: Tag[] }) => {
|
||||
const list = value.map(tag => {
|
||||
return (
|
||||
@@ -16,10 +17,7 @@ export const Tags = ({ value }: { value: Tag[] }) => {
|
||||
);
|
||||
});
|
||||
return (
|
||||
<Menu
|
||||
pointerEnterDelay={500}
|
||||
content={<div className={styles.tagListFull}>{list}</div>}
|
||||
>
|
||||
<Menu items={<div className={styles.tagListFull}>{list}</div>}>
|
||||
<div className={styles.tagList}>{list}</div>
|
||||
</Menu>
|
||||
);
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import type { Filter, Literal } from '@affine/env/filter';
|
||||
import type { PropertiesMeta } from '@affine/env/filter';
|
||||
import { Menu, MenuItem } from '@toeverything/components/menu';
|
||||
import type { ReactNode } from 'react';
|
||||
import { useMemo } from 'react';
|
||||
|
||||
import { Menu, MenuItem } from '../../../ui/menu';
|
||||
import { FilterTag } from './filter-tag-translation';
|
||||
import * as styles from './index.css';
|
||||
import { literalMatcher } from './literal-matcher';
|
||||
@@ -50,8 +50,7 @@ export const Condition = ({
|
||||
style={{ display: 'flex', userSelect: 'none', alignItems: 'center' }}
|
||||
>
|
||||
<Menu
|
||||
trigger="click"
|
||||
content={
|
||||
items={
|
||||
<VariableSelect
|
||||
propertiesMeta={propertiesMeta}
|
||||
selected={[]}
|
||||
@@ -67,8 +66,7 @@ export const Condition = ({
|
||||
</div>
|
||||
</Menu>
|
||||
<Menu
|
||||
trigger="click"
|
||||
content={
|
||||
items={
|
||||
<FunctionSelect
|
||||
propertiesMeta={propertiesMeta}
|
||||
value={value}
|
||||
|
||||
@@ -4,8 +4,8 @@ import { useAFFiNEI18N } from '@affine/i18n/hooks';
|
||||
import { CloseIcon, PlusIcon } from '@blocksuite/icons';
|
||||
import { Button } from '@toeverything/components/button';
|
||||
import { IconButton } from '@toeverything/components/button';
|
||||
import { Menu } from '@toeverything/components/menu';
|
||||
|
||||
import { Menu } from '../../..';
|
||||
import { Condition } from './condition';
|
||||
import * as styles from './index.css';
|
||||
import { CreateFilterMenu } from './vars';
|
||||
@@ -53,8 +53,7 @@ export const FilterList = ({
|
||||
);
|
||||
})}
|
||||
<Menu
|
||||
trigger={'click'}
|
||||
content={
|
||||
items={
|
||||
<CreateFilterMenu
|
||||
value={value}
|
||||
onChange={onChange}
|
||||
|
||||
@@ -4,7 +4,7 @@ type FilterTagProps = {
|
||||
name: string;
|
||||
};
|
||||
|
||||
export const FilterTag = ({ name }: FilterTagProps) => {
|
||||
const useFilterTag = ({ name }: FilterTagProps) => {
|
||||
const t = useAFFiNEI18N();
|
||||
switch (name) {
|
||||
case 'Created':
|
||||
@@ -41,3 +41,9 @@ export const FilterTag = ({ name }: FilterTagProps) => {
|
||||
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 { useMemo } from 'react';
|
||||
|
||||
import Menu from '../../../ui/menu/menu';
|
||||
import * as styles from './multi-select.css';
|
||||
|
||||
export const MultiSelect = ({
|
||||
@@ -21,10 +20,10 @@ export const MultiSelect = ({
|
||||
() => Object.fromEntries(options.map(v => [v.value, v])),
|
||||
[options]
|
||||
);
|
||||
|
||||
return (
|
||||
<Menu
|
||||
trigger="click"
|
||||
content={
|
||||
items={
|
||||
<div data-testid="multi-select" className={styles.optionList}>
|
||||
{options.map(option => {
|
||||
const selected = value.includes(option.value);
|
||||
@@ -37,25 +36,14 @@ export const MultiSelect = ({
|
||||
}
|
||||
};
|
||||
return (
|
||||
<div
|
||||
className={styles.selectOption}
|
||||
data-testid="select-option"
|
||||
style={{
|
||||
backgroundColor: selected
|
||||
? 'var(--affine-hover-color)'
|
||||
: undefined,
|
||||
}}
|
||||
<MenuItem
|
||||
data-testid={`multi-select-${option.label}`}
|
||||
checked={selected}
|
||||
onClick={click}
|
||||
key={option.value}
|
||||
>
|
||||
<div className={styles.optionLabel}>{option.label}</div>
|
||||
<div
|
||||
style={{ opacity: selected ? 1 : 0 }}
|
||||
className={styles.done}
|
||||
>
|
||||
<DoneIcon />
|
||||
</div>
|
||||
</div>
|
||||
{option.label}
|
||||
</MenuItem>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
|
||||
@@ -5,10 +5,10 @@ import type {
|
||||
VariableMap,
|
||||
} from '@affine/env/filter';
|
||||
import { useAFFiNEI18N } from '@affine/i18n/hooks';
|
||||
import { MenuIcon, MenuItem } from '@toeverything/components/menu';
|
||||
import dayjs from 'dayjs';
|
||||
import type { ReactNode } from 'react';
|
||||
|
||||
import { MenuItem } from '../../../ui/menu';
|
||||
import { FilterTag } from './filter-tag-translation';
|
||||
import * as styles from './index.css';
|
||||
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))
|
||||
.map(v => (
|
||||
<MenuItem
|
||||
icon={variableDefineMap[v.name].icon}
|
||||
preFix={<MenuIcon>{variableDefineMap[v.name].icon}</MenuIcon>}
|
||||
key={v.name}
|
||||
onClick={() => {
|
||||
onSelect(createDefaultFilter(v, propertiesMeta));
|
||||
|
||||
@@ -9,10 +9,11 @@ import {
|
||||
ResetIcon,
|
||||
} from '@blocksuite/icons';
|
||||
import { IconButton } from '@toeverything/components/button';
|
||||
import { Menu, MenuIcon, MenuItem } from '@toeverything/components/menu';
|
||||
import { Tooltip } from '@toeverything/components/tooltip';
|
||||
import { useState } from 'react';
|
||||
|
||||
import { Confirm, FlexWrapper, Menu, MenuItem } from '../../..';
|
||||
import { Confirm, FlexWrapper } from '../../..';
|
||||
import { DisablePublicSharing, MoveToTrash } from './operation-menu-items';
|
||||
|
||||
export interface OperationCellProps {
|
||||
@@ -43,31 +44,40 @@ export const OperationCell = ({
|
||||
{isPublic && (
|
||||
<DisablePublicSharing
|
||||
data-testid="disable-public-sharing"
|
||||
onItemClick={() => {
|
||||
onSelect={() => {
|
||||
setOpenDisableShared(true);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
<MenuItem
|
||||
onClick={onToggleFavoritePage}
|
||||
icon={
|
||||
favorite ? (
|
||||
<FavoritedIcon style={{ color: 'var(--affine-primary-color)' }} />
|
||||
) : (
|
||||
<FavoriteIcon />
|
||||
)
|
||||
preFix={
|
||||
<MenuIcon>
|
||||
{favorite ? (
|
||||
<FavoritedIcon style={{ color: 'var(--affine-primary-color)' }} />
|
||||
) : (
|
||||
<FavoriteIcon />
|
||||
)}
|
||||
</MenuIcon>
|
||||
}
|
||||
>
|
||||
{favorite ? t['Remove from favorites']() : t['Add to Favorites']()}
|
||||
</MenuItem>
|
||||
{!isDesktop && (
|
||||
<MenuItem onClick={onOpenPageInNewTab} icon={<OpenInNewIcon />}>
|
||||
<MenuItem
|
||||
onClick={onOpenPageInNewTab}
|
||||
preFix={
|
||||
<MenuIcon>
|
||||
<OpenInNewIcon />
|
||||
</MenuIcon>
|
||||
}
|
||||
>
|
||||
{t['Open in new tab']()}
|
||||
</MenuItem>
|
||||
)}
|
||||
<MoveToTrash
|
||||
data-testid="move-to-trash"
|
||||
onItemClick={() => {
|
||||
onSelect={() => {
|
||||
setOpen(true);
|
||||
}}
|
||||
/>
|
||||
@@ -76,13 +86,8 @@ export const OperationCell = ({
|
||||
return (
|
||||
<>
|
||||
<FlexWrapper alignItems="center" justifyContent="center">
|
||||
<Menu
|
||||
content={OperationMenu}
|
||||
placement="bottom"
|
||||
disablePortal={true}
|
||||
trigger="click"
|
||||
>
|
||||
<IconButton data-testid="page-list-operation-button">
|
||||
<Menu items={OperationMenu}>
|
||||
<IconButton type="plain" data-testid="page-list-operation-button">
|
||||
<MoreVerticalIcon />
|
||||
</IconButton>
|
||||
</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 { ShareIcon } from '@blocksuite/icons';
|
||||
import {
|
||||
MenuIcon,
|
||||
MenuItem,
|
||||
type MenuItemProps,
|
||||
} from '@toeverything/components/menu';
|
||||
|
||||
import { MenuItem, styled } from '../../../';
|
||||
import { PublicLinkDisableModal } from '../../share-menu';
|
||||
import type { CommonMenuItemProps } from './types';
|
||||
|
||||
const StyledMenuItem = styled(MenuItem)(({ theme }) => {
|
||||
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) => {
|
||||
export const DisablePublicSharing = (props: MenuItemProps) => {
|
||||
const t = useAFFiNEI18N();
|
||||
return (
|
||||
<>
|
||||
<StyledMenuItem
|
||||
{...props}
|
||||
onClick={() => {
|
||||
onItemClick?.();
|
||||
onSelect?.();
|
||||
}}
|
||||
style={{ color: 'red' }}
|
||||
icon={<ShareIcon />}
|
||||
>
|
||||
{t['Disable Public Sharing']()}
|
||||
</StyledMenuItem>
|
||||
</>
|
||||
<MenuItem
|
||||
type="danger"
|
||||
preFix={
|
||||
<MenuIcon>
|
||||
<ShareIcon />
|
||||
</MenuIcon>
|
||||
}
|
||||
{...props}
|
||||
>
|
||||
{t['Disable Public Sharing']()}
|
||||
</MenuItem>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -2,36 +2,22 @@ import { pushNotificationAtom } from '@affine/component/notification-center';
|
||||
import { useAFFiNEI18N } from '@affine/i18n/hooks';
|
||||
import type { PageBlockModel } from '@blocksuite/blocks';
|
||||
import {
|
||||
ArrowRightSmallIcon,
|
||||
ExportIcon,
|
||||
ExportToHtmlIcon,
|
||||
ExportToMarkdownIcon,
|
||||
ExportToPdfIcon,
|
||||
ExportToPngIcon,
|
||||
} from '@blocksuite/icons';
|
||||
import { MenuIcon, MenuItem, MenuSub } from '@toeverything/components/menu';
|
||||
import { useSetAtom } from 'jotai';
|
||||
import { useCallback } from 'react';
|
||||
|
||||
import { Menu, MenuItem } from '../../..';
|
||||
import { getContentParser } from './get-content-parser';
|
||||
import type { CommonMenuItemProps } from './types';
|
||||
|
||||
type ExportMenuItemProps = {
|
||||
iconSize?: number;
|
||||
gap?: string;
|
||||
fontSize?: string;
|
||||
};
|
||||
const MenuItemStyle = {
|
||||
padding: '4px 12px',
|
||||
};
|
||||
|
||||
export const ExportToPdfMenuItem = ({
|
||||
onSelect,
|
||||
style = MenuItemStyle,
|
||||
iconSize,
|
||||
gap,
|
||||
fontSize,
|
||||
}: CommonMenuItemProps<{ type: 'pdf' }> & ExportMenuItemProps) => {
|
||||
}: CommonMenuItemProps<{ type: 'pdf' }>) => {
|
||||
const t = useAFFiNEI18N();
|
||||
const { currentEditor } = globalThis;
|
||||
const setPushNotification = useSetAtom(pushNotificationAtom);
|
||||
@@ -89,12 +75,12 @@ export const ExportToPdfMenuItem = ({
|
||||
return (
|
||||
<MenuItem
|
||||
data-testid="export-to-pdf"
|
||||
onClick={onClickDownloadPDF}
|
||||
icon={<ExportToPdfIcon />}
|
||||
style={style}
|
||||
iconSize={iconSize}
|
||||
gap={gap}
|
||||
fontSize={fontSize}
|
||||
onSelect={onClickDownloadPDF}
|
||||
preFix={
|
||||
<MenuIcon>
|
||||
<ExportToPdfIcon />
|
||||
</MenuIcon>
|
||||
}
|
||||
>
|
||||
{t['Export to PDF']()}
|
||||
</MenuItem>
|
||||
@@ -103,11 +89,7 @@ export const ExportToPdfMenuItem = ({
|
||||
|
||||
export const ExportToHtmlMenuItem = ({
|
||||
onSelect,
|
||||
style = MenuItemStyle,
|
||||
iconSize,
|
||||
gap,
|
||||
fontSize,
|
||||
}: CommonMenuItemProps<{ type: 'html' }> & ExportMenuItemProps) => {
|
||||
}: CommonMenuItemProps<{ type: 'html' }>) => {
|
||||
const t = useAFFiNEI18N();
|
||||
const { currentEditor } = globalThis;
|
||||
const setPushNotification = useSetAtom(pushNotificationAtom);
|
||||
@@ -140,12 +122,12 @@ export const ExportToHtmlMenuItem = ({
|
||||
<>
|
||||
<MenuItem
|
||||
data-testid="export-to-html"
|
||||
onClick={onClickExportHtml}
|
||||
icon={<ExportToHtmlIcon />}
|
||||
style={style}
|
||||
iconSize={iconSize}
|
||||
gap={gap}
|
||||
fontSize={fontSize}
|
||||
onSelect={onClickExportHtml}
|
||||
preFix={
|
||||
<MenuIcon>
|
||||
<ExportToHtmlIcon />
|
||||
</MenuIcon>
|
||||
}
|
||||
>
|
||||
{t['Export to HTML']()}
|
||||
</MenuItem>
|
||||
@@ -155,11 +137,7 @@ export const ExportToHtmlMenuItem = ({
|
||||
|
||||
export const ExportToPngMenuItem = ({
|
||||
onSelect,
|
||||
style = MenuItemStyle,
|
||||
iconSize,
|
||||
gap,
|
||||
fontSize,
|
||||
}: CommonMenuItemProps<{ type: 'png' }> & ExportMenuItemProps) => {
|
||||
}: CommonMenuItemProps<{ type: 'png' }>) => {
|
||||
const t = useAFFiNEI18N();
|
||||
const { currentEditor } = globalThis;
|
||||
const setPushNotification = useSetAtom(pushNotificationAtom);
|
||||
@@ -194,12 +172,12 @@ export const ExportToPngMenuItem = ({
|
||||
<>
|
||||
<MenuItem
|
||||
data-testid="export-to-png"
|
||||
onClick={onClickDownloadPNG}
|
||||
icon={<ExportToPngIcon />}
|
||||
style={style}
|
||||
iconSize={iconSize}
|
||||
gap={gap}
|
||||
fontSize={fontSize}
|
||||
onSelect={onClickDownloadPNG}
|
||||
preFix={
|
||||
<MenuIcon>
|
||||
<ExportToPngIcon />
|
||||
</MenuIcon>
|
||||
}
|
||||
>
|
||||
{t['Export to PNG']()}
|
||||
</MenuItem>
|
||||
@@ -209,11 +187,7 @@ export const ExportToPngMenuItem = ({
|
||||
|
||||
export const ExportToMarkdownMenuItem = ({
|
||||
onSelect,
|
||||
style = MenuItemStyle,
|
||||
iconSize,
|
||||
gap,
|
||||
fontSize,
|
||||
}: CommonMenuItemProps<{ type: 'markdown' }> & ExportMenuItemProps) => {
|
||||
}: CommonMenuItemProps<{ type: 'markdown' }>) => {
|
||||
const t = useAFFiNEI18N();
|
||||
const { currentEditor } = globalThis;
|
||||
const setPushNotification = useSetAtom(pushNotificationAtom);
|
||||
@@ -246,12 +220,12 @@ export const ExportToMarkdownMenuItem = ({
|
||||
<>
|
||||
<MenuItem
|
||||
data-testid="export-to-markdown"
|
||||
onClick={onClickExportMarkdown}
|
||||
icon={<ExportToMarkdownIcon />}
|
||||
style={style}
|
||||
iconSize={iconSize}
|
||||
gap={gap}
|
||||
fontSize={fontSize}
|
||||
onSelect={onClickExportMarkdown}
|
||||
preFix={
|
||||
<MenuIcon>
|
||||
<ExportToMarkdownIcon />
|
||||
</MenuIcon>
|
||||
}
|
||||
>
|
||||
{t['Export to Markdown']()}
|
||||
</MenuItem>
|
||||
@@ -259,16 +233,12 @@ export const ExportToMarkdownMenuItem = ({
|
||||
);
|
||||
};
|
||||
|
||||
export const Export = ({
|
||||
onItemClick,
|
||||
}: CommonMenuItemProps<{ type: 'markdown' | 'html' | 'pdf' | 'png' }>) => {
|
||||
// fixme: refactor this file, export function may should be passed by 'props', this file is just a ui component
|
||||
export const Export = () => {
|
||||
const t = useAFFiNEI18N();
|
||||
return (
|
||||
<Menu
|
||||
width={248}
|
||||
trigger="hover"
|
||||
placement="right-start"
|
||||
content={
|
||||
<MenuSub
|
||||
items={
|
||||
<>
|
||||
<ExportToPdfMenuItem></ExportToPdfMenuItem>
|
||||
<ExportToHtmlMenuItem></ExportToHtmlMenuItem>
|
||||
@@ -276,24 +246,16 @@ export const Export = ({
|
||||
<ExportToMarkdownMenuItem></ExportToMarkdownMenuItem>
|
||||
</>
|
||||
}
|
||||
menuStyles={{
|
||||
borderRadius: '8px',
|
||||
padding: '8px',
|
||||
background: 'var(--affine-background-overlay-panel-color)',
|
||||
triggerOptions={{
|
||||
preFix: (
|
||||
<MenuIcon>
|
||||
<ExportIcon />
|
||||
</MenuIcon>
|
||||
),
|
||||
['data-testid' as string]: 'export-menu',
|
||||
}}
|
||||
>
|
||||
<MenuItem
|
||||
data-testid="export-menu"
|
||||
icon={<ExportIcon />}
|
||||
endIcon={<ArrowRightSmallIcon />}
|
||||
style={MenuItemStyle}
|
||||
onClick={e => {
|
||||
e.stopPropagation();
|
||||
onItemClick?.();
|
||||
}}
|
||||
>
|
||||
{t.Export()}
|
||||
</MenuItem>
|
||||
</Menu>
|
||||
{t.Export()}
|
||||
</MenuSub>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
export * from './copy-link';
|
||||
export * from './disable-public-sharing';
|
||||
export * from './export';
|
||||
// export * from './MoveTo';
|
||||
|
||||
@@ -1,31 +1,28 @@
|
||||
import { useAFFiNEI18N } from '@affine/i18n/hooks';
|
||||
import { DeleteTemporarilyIcon } from '@blocksuite/icons';
|
||||
import {
|
||||
MenuIcon,
|
||||
MenuItem,
|
||||
type MenuItemProps,
|
||||
} from '@toeverything/components/menu';
|
||||
|
||||
import type { ConfirmProps } from '../../..';
|
||||
import { Confirm, MenuItem } from '../../..';
|
||||
import { moveToTrashStyle } from './index.css';
|
||||
import type { CommonMenuItemProps } from './types';
|
||||
export const MoveToTrash = ({
|
||||
onSelect,
|
||||
onItemClick,
|
||||
...props
|
||||
}: CommonMenuItemProps) => {
|
||||
import { Confirm } from '../../..';
|
||||
export const MoveToTrash = (props: MenuItemProps) => {
|
||||
const t = useAFFiNEI18N();
|
||||
|
||||
return (
|
||||
<>
|
||||
<MenuItem
|
||||
{...props}
|
||||
onClick={() => {
|
||||
onItemClick?.();
|
||||
onSelect?.();
|
||||
}}
|
||||
icon={<DeleteTemporarilyIcon />}
|
||||
className={moveToTrashStyle}
|
||||
>
|
||||
{t['Move to Trash']()}
|
||||
</MenuItem>
|
||||
</>
|
||||
<MenuItem
|
||||
preFix={
|
||||
<MenuIcon>
|
||||
<DeleteTemporarilyIcon />
|
||||
</MenuIcon>
|
||||
}
|
||||
type="danger"
|
||||
{...props}
|
||||
>
|
||||
{t['Move to Trash']()}
|
||||
</MenuItem>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -6,13 +6,12 @@ import type { GetPageInfoById } from '@affine/env/page-info';
|
||||
import { useAFFiNEI18N } from '@affine/i18n/hooks';
|
||||
import { FilteredIcon, FolderIcon, ViewLayersIcon } from '@blocksuite/icons';
|
||||
import { Button } from '@toeverything/components/button';
|
||||
import { Menu, MenuIcon, MenuItem } from '@toeverything/components/menu';
|
||||
import { Tooltip } from '@toeverything/components/tooltip';
|
||||
import clsx from 'clsx';
|
||||
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 type { useCollectionManager } from '../use-collection-manager';
|
||||
import * as styles from './collection-list.css';
|
||||
@@ -40,7 +39,11 @@ const CollectionOption = ({
|
||||
return (
|
||||
<MenuItem
|
||||
data-testid="collection-select-option"
|
||||
icon={<ViewLayersIcon></ViewLayersIcon>}
|
||||
preFix={
|
||||
<MenuIcon>
|
||||
<ViewLayersIcon />
|
||||
</MenuIcon>
|
||||
}
|
||||
onClick={selectCollection}
|
||||
key={collection.id}
|
||||
className={styles.viewMenu}
|
||||
@@ -107,6 +110,7 @@ export const CollectionList = ({
|
||||
getPageInfo: GetPageInfoById;
|
||||
propertiesMeta: PropertiesMeta;
|
||||
}) => {
|
||||
const ref = useRef(null);
|
||||
const t = useAFFiNEI18N();
|
||||
const [collection, setCollection] = useState<Collection>();
|
||||
const onChange = useCallback(
|
||||
@@ -134,14 +138,20 @@ export const CollectionList = ({
|
||||
[closeUpdateCollectionModal, setting]
|
||||
);
|
||||
return (
|
||||
<FlexWrapper alignItems="center">
|
||||
<FlexWrapper alignItems="center" ref={ref}>
|
||||
{setting.savedCollections.length > 0 && (
|
||||
<Menu
|
||||
trigger="click"
|
||||
content={
|
||||
portalOptions={{
|
||||
container: ref.current,
|
||||
}}
|
||||
items={
|
||||
<div style={{ minWidth: 150 }}>
|
||||
<MenuItem
|
||||
icon={<FolderIcon></FolderIcon>}
|
||||
preFix={
|
||||
<MenuIcon>
|
||||
<FolderIcon />
|
||||
</MenuIcon>
|
||||
}
|
||||
onClick={setting.backToAll}
|
||||
className={styles.viewMenu}
|
||||
>
|
||||
@@ -184,17 +194,22 @@ export const CollectionList = ({
|
||||
</Menu>
|
||||
)}
|
||||
<Menu
|
||||
trigger="click"
|
||||
placement="bottom-start"
|
||||
content={
|
||||
items={
|
||||
<CreateFilterMenu
|
||||
propertiesMeta={propertiesMeta}
|
||||
value={setting.currentCollection.filterList}
|
||||
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']()}
|
||||
</Button>
|
||||
</Menu>
|
||||
|
||||
@@ -17,26 +17,10 @@ export const ShareExport = () => {
|
||||
{t['com.affine.share-menu.ShareViaExport']()}
|
||||
</div>
|
||||
<div>
|
||||
<ExportToPdfMenuItem
|
||||
style={{ padding: '4px' }}
|
||||
iconSize={16}
|
||||
gap={'4px'}
|
||||
/>
|
||||
<ExportToHtmlMenuItem
|
||||
style={{ padding: '4px' }}
|
||||
iconSize={16}
|
||||
gap={'4px'}
|
||||
/>
|
||||
<ExportToPngMenuItem
|
||||
style={{ padding: '4px' }}
|
||||
iconSize={16}
|
||||
gap={'4px'}
|
||||
/>
|
||||
<ExportToMarkdownMenuItem
|
||||
style={{ padding: '4px' }}
|
||||
iconSize={16}
|
||||
gap={'4px'}
|
||||
/>
|
||||
<ExportToPdfMenuItem />
|
||||
<ExportToHtmlMenuItem />
|
||||
<ExportToPngMenuItem />
|
||||
<ExportToMarkdownMenuItem />
|
||||
</div>
|
||||
<div className={styles.columnContainerStyle}>
|
||||
<div className={styles.descriptionStyle}>
|
||||
|
||||
@@ -8,12 +8,10 @@ import { useAFFiNEI18N } from '@affine/i18n/hooks';
|
||||
import type { Page } from '@blocksuite/store';
|
||||
import { Button } from '@toeverything/components/button';
|
||||
import { Divider } from '@toeverything/components/divider';
|
||||
import { useAtom } from 'jotai';
|
||||
import { useCallback } from 'react';
|
||||
import { Menu } from '@toeverything/components/menu';
|
||||
import { useRef } from 'react';
|
||||
|
||||
import { Menu } from '../../ui/menu/menu';
|
||||
import * as styles from './index.css';
|
||||
import { enableShareMenuAtom } from './index.jotai';
|
||||
import { ShareExport } from './share-export';
|
||||
import { SharePage } from './share-page';
|
||||
|
||||
@@ -35,11 +33,11 @@ export interface ShareMenuProps<
|
||||
|
||||
export const ShareMenu = (props: ShareMenuProps) => {
|
||||
const { useIsSharedPage } = props;
|
||||
const ref = useRef(null);
|
||||
const isSharedPage = useIsSharedPage(
|
||||
props.workspace.id,
|
||||
props.currentPage.id
|
||||
);
|
||||
const [open, setOpen] = useAtom(enableShareMenuAtom);
|
||||
const t = useAFFiNEI18N();
|
||||
const content = (
|
||||
<div className={styles.containerStyle}>
|
||||
@@ -52,28 +50,12 @@ export const ShareMenu = (props: ShareMenuProps) => {
|
||||
);
|
||||
return (
|
||||
<Menu
|
||||
menuStyles={{
|
||||
padding: '12px',
|
||||
background: 'var(--affine-background-overlay-panel-color)',
|
||||
transform: 'translateX(-10px)',
|
||||
items={content}
|
||||
portalOptions={{
|
||||
container: ref.current,
|
||||
}}
|
||||
content={content}
|
||||
visible={open}
|
||||
placement="bottom"
|
||||
trigger={['click']}
|
||||
width={410}
|
||||
disablePortal={true}
|
||||
onClickAway={useCallback(() => {
|
||||
setOpen(false);
|
||||
}, [setOpen])}
|
||||
>
|
||||
<Button
|
||||
data-testid="share-menu-button"
|
||||
onClick={useCallback(() => {
|
||||
setOpen(value => !value);
|
||||
}, [setOpen])}
|
||||
type={'plain'}
|
||||
>
|
||||
<Button data-testid="share-menu-button" type="plain" ref={ref}>
|
||||
<div
|
||||
style={{
|
||||
color: isSharedPage
|
||||
|
||||
@@ -1,15 +1,9 @@
|
||||
import {
|
||||
Menu,
|
||||
MenuItem,
|
||||
MenuTrigger,
|
||||
RadioButton,
|
||||
RadioButtonGroup,
|
||||
Switch,
|
||||
} from '@affine/component';
|
||||
import { RadioButton, RadioButtonGroup, Switch } from '@affine/component';
|
||||
import { WorkspaceFlavour } from '@affine/env/workspace';
|
||||
import { useAFFiNEI18N } from '@affine/i18n/hooks';
|
||||
import { ArrowRightSmallIcon, WebIcon } from '@blocksuite/icons';
|
||||
import { Button } from '@toeverything/components/button';
|
||||
import { Menu, MenuItem, MenuTrigger } from '@toeverything/components/menu';
|
||||
import { useState } from 'react';
|
||||
import { useCallback, useMemo } from 'react';
|
||||
|
||||
@@ -186,20 +180,8 @@ export const AffineSharePage = (props: ShareMenuProps) => {
|
||||
<div className={styles.rowContainerStyle}>
|
||||
<div className={styles.subTitleStyle}>Link expires</div>
|
||||
<div>
|
||||
<Menu
|
||||
content={<MenuItem>Never</MenuItem>}
|
||||
placement="bottom-end"
|
||||
trigger="click"
|
||||
>
|
||||
<MenuTrigger
|
||||
style={{
|
||||
display: 'flex',
|
||||
justifyContent: 'space-between',
|
||||
padding: '4px 6px 4px 10px',
|
||||
}}
|
||||
>
|
||||
Never
|
||||
</MenuTrigger>
|
||||
<Menu items={<MenuItem>Never</MenuItem>}>
|
||||
<MenuTrigger>Never</MenuTrigger>
|
||||
</Menu>
|
||||
</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-trigger';
|
||||
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;
|
||||
Reference in New Issue
Block a user