feat: support header right slot

This commit is contained in:
QiShaoXuan
2022-12-21 15:31:35 +08:00
parent 954298480b
commit a453454250
8 changed files with 196 additions and 163 deletions
@@ -17,7 +17,7 @@ export const EditorHeader = () => {
const [title, setTitle] = useState('');
const [isHover, setIsHover] = useState(false);
const { editor } = useAppState();
const pageMeta = useCurrentPageMeta();
const { trash: isTrash = false } = useCurrentPageMeta() || {};
const onPropsUpdated = usePropsUpdated();
useEffect(() => {
@@ -34,16 +34,22 @@ export const EditorHeader = () => {
}, [editor]);
return (
<Header>
<Header
rightItems={
isTrash
? ['trashButtonGroup']
: ['syncUser', 'themeModeSwitch', 'editorOptionMenu']
}
>
{title && (
<StyledTitle
onMouseEnter={() => {
if (pageMeta?.trash) return;
if (isTrash) return;
setIsHover(true);
}}
onMouseLeave={() => {
if (pageMeta?.trash) return;
if (isTrash) return;
setIsHover(false);
}}
@@ -0,0 +1,108 @@
import { Menu, MenuItem } from '@/ui/menu';
import { IconButton } from '@/ui/button';
import {
EdgelessIcon,
ExportIcon,
ExportToHtmlIcon,
ExportToMarkdownIcon,
FavouritedIcon,
FavouritesIcon,
MoreVerticalIcon,
PaperIcon,
TrashIcon,
} from '@blocksuite/icons';
import { useAppState } from '@/providers/app-state-provider';
import { usePageHelper } from '@/hooks/use-page-helper';
import { useConfirm } from '@/providers/confirm-provider';
import useCurrentPageMeta from '@/hooks/use-current-page-meta';
import { toast } from '@/ui/toast';
const PopoverContent = () => {
const { editor } = useAppState();
const { toggleFavoritePage, toggleDeletePage } = usePageHelper();
const { changePageMode } = usePageHelper();
const { confirm } = useConfirm();
const {
mode = 'page',
id = '',
favorite = false,
title = '',
} = useCurrentPageMeta() || {};
return (
<>
<MenuItem
onClick={() => {
toggleFavoritePage(id);
toast(!favorite ? 'Removed to Favourites' : 'Added to Favourites');
}}
icon={favorite ? <FavouritedIcon /> : <FavouritesIcon />}
>
{favorite ? 'Remove' : 'Add'} to favourites
</MenuItem>
<MenuItem
icon={mode === 'page' ? <EdgelessIcon /> : <PaperIcon />}
onClick={() => {
changePageMode(id, mode === 'page' ? 'edgeless' : 'page');
}}
>
Convert to {mode === 'page' ? 'Edgeless' : 'Page'}
</MenuItem>
<Menu
placement="left-start"
content={
<>
<MenuItem
onClick={() => {
editor && editor.contentParser.onExportHtml();
}}
icon={<ExportToHtmlIcon />}
>
Export to HTML
</MenuItem>
<MenuItem
onClick={() => {
editor && editor.contentParser.onExportMarkdown();
}}
icon={<ExportToMarkdownIcon />}
>
Export to Markdown
</MenuItem>
</>
}
>
<MenuItem icon={<ExportIcon />} isDir={true}>
Export
</MenuItem>
</Menu>
<MenuItem
onClick={() => {
confirm({
title: 'Delete page?',
content: `${title || 'Untitled'} will be moved to Trash`,
confirmText: 'Delete',
confirmType: 'danger',
}).then(confirm => {
confirm && toggleDeletePage(id);
toast('Moved to Trash');
});
}}
icon={<TrashIcon />}
>
Delete
</MenuItem>
</>
);
};
export const EditorOptionMenu = () => {
return (
<Menu content={<PopoverContent />} placement="bottom-end">
<IconButton>
<MoreVerticalIcon />
</IconButton>
</Menu>
);
};
export default EditorOptionMenu;
@@ -3,7 +3,7 @@ import { useModal } from '@/providers/global-modal-provider';
import { useAppState } from '@/providers/app-state-provider/context';
import { IconButton } from '@/ui/button';
export const SyncIcon = () => {
export const SyncUser = () => {
const { triggerLoginModal } = useModal();
const appState = useAppState();
@@ -17,3 +17,5 @@ export const SyncIcon = () => {
</IconButton>
);
};
export default SyncUser;
@@ -0,0 +1,53 @@
import { Button } from '@/ui/button';
import { usePageHelper } from '@/hooks/use-page-helper';
import { useAppState } from '@/providers/app-state-provider';
import { useConfirm } from '@/providers/confirm-provider';
import { useRouter } from 'next/router';
import useCurrentPageMeta from '@/hooks/use-current-page-meta';
export const TrashButtonGroup = () => {
const { permanentlyDeletePage } = usePageHelper();
const { currentWorkspaceId } = useAppState();
const { toggleDeletePage } = usePageHelper();
const { confirm } = useConfirm();
const router = useRouter();
const { id = '' } = useCurrentPageMeta() || {};
return (
<>
<Button
bold={true}
shape="round"
style={{ marginRight: '24px' }}
onClick={() => {
toggleDeletePage(id);
}}
>
Restore it
</Button>
<Button
bold={true}
shape="round"
type="danger"
onClick={() => {
confirm({
title: 'Permanently delete',
content:
"Once deleted, you can't undo this action. Do you confirm?",
confirmText: 'Delete',
confirmType: 'danger',
}).then(confirm => {
if (confirm) {
router.push(`/workspace/${currentWorkspaceId}/all`);
permanentlyDeletePage(id);
}
});
}}
>
Delete permanently
</Button>
</>
);
};
export default TrashButtonGroup;
+22 -158
View File
@@ -1,4 +1,4 @@
import React, { PropsWithChildren, useState } from 'react';
import React, { PropsWithChildren, ReactNode, useState } from 'react';
import {
StyledHeader,
StyledHeaderRightSide,
@@ -6,106 +6,12 @@ import {
StyledBrowserWarning,
StyledCloseButton,
} from './styles';
import {
ExportIcon,
EdgelessIcon,
PaperIcon,
ExportToHtmlIcon,
ExportToMarkdownIcon,
MoreVerticalIcon,
FavouritesIcon,
FavouritedIcon,
TrashIcon,
} from '@blocksuite/icons';
import { useAppState } from '@/providers/app-state-provider/context';
import ThemeModeSwitch from '@/components/theme-mode-switch';
import { IconButton, Button } from '@/ui/button';
import CloseIcon from '@mui/icons-material/Close';
import { getWarningMessage, shouldShowWarning } from './utils';
import { Menu, MenuItem } from '@/ui/menu';
import { useRouter } from 'next/router';
import { useConfirm } from '@/providers/confirm-provider';
import { SyncIcon } from './sync-icon';
import { toast } from '@/ui/toast';
import useCurrentPageMeta from '@/hooks/use-current-page-meta';
import { usePageHelper } from '@/hooks/use-page-helper';
const PopoverContent = () => {
const { editor } = useAppState();
const { toggleFavoritePage, toggleDeletePage } = usePageHelper();
const { changePageMode } = usePageHelper();
const { confirm } = useConfirm();
const {
mode = 'page',
id = '',
favorite = false,
title = '',
} = useCurrentPageMeta() || {};
return (
<>
<MenuItem
onClick={() => {
toggleFavoritePage(id);
toast(!favorite ? 'Removed to Favourites' : 'Added to Favourites');
}}
icon={favorite ? <FavouritedIcon /> : <FavouritesIcon />}
>
{favorite ? 'Remove' : 'Add'} to favourites
</MenuItem>
<MenuItem
icon={mode === 'page' ? <EdgelessIcon /> : <PaperIcon />}
onClick={() => {
changePageMode(id, mode === 'page' ? 'edgeless' : 'page');
}}
>
Convert to {mode === 'page' ? 'Edgeless' : 'Page'}
</MenuItem>
<Menu
placement="left-start"
content={
<>
<MenuItem
onClick={() => {
editor && editor.contentParser.onExportHtml();
}}
icon={<ExportToHtmlIcon />}
>
Export to HTML
</MenuItem>
<MenuItem
onClick={() => {
editor && editor.contentParser.onExportMarkdown();
}}
icon={<ExportToMarkdownIcon />}
>
Export to Markdown
</MenuItem>
</>
}
>
<MenuItem icon={<ExportIcon />} isDir={true}>
Export
</MenuItem>
</Menu>
<MenuItem
onClick={() => {
confirm({
title: 'Delete page?',
content: `${title || 'Untitled'} will be moved to Trash`,
confirmText: 'Delete',
confirmType: 'danger',
}).then(confirm => {
confirm && toggleDeletePage(id);
toast('Moved to Trash');
});
}}
icon={<TrashIcon />}
>
Delete
</MenuItem>
</>
);
};
import EditorOptionMenu from './header-right-items/editor-option-menu';
import TrashButtonGroup from './header-right-items/trash-button-group';
import ThemeModeSwitch from './header-right-items/theme-mode-switch';
import SyncUser from './header-right-items/sync-user';
const BrowserWarning = ({
show,
@@ -124,67 +30,23 @@ const BrowserWarning = ({
);
};
const HeaderRight = () => {
const { permanentlyDeletePage } = usePageHelper();
const { currentWorkspaceId } = useAppState();
const { toggleDeletePage } = usePageHelper();
const { confirm } = useConfirm();
const router = useRouter();
const currentPageMeta = useCurrentPageMeta();
const isTrash = !!currentPageMeta?.trash;
type HeaderRightItemNames =
| 'editorOptionMenu'
| 'trashButtonGroup'
| 'themeModeSwitch'
| 'syncUser';
if (isTrash) {
const { id } = currentPageMeta;
return (
<>
<Button
bold={true}
shape="round"
style={{ marginRight: '24px' }}
onClick={() => {
toggleDeletePage(id);
}}
>
Restore it
</Button>
<Button
bold={true}
shape="round"
type="danger"
onClick={() => {
confirm({
title: 'Permanently delete',
content:
"Once deleted, you can't undo this action. Do you confirm?",
confirmText: 'Delete',
confirmType: 'danger',
}).then(confirm => {
if (confirm) {
router.push(`/workspace/${currentWorkspaceId}/all`);
permanentlyDeletePage(id);
}
});
}}
>
Delete permanently
</Button>
</>
);
}
return (
<>
<SyncIcon />
<ThemeModeSwitch />
<Menu content={<PopoverContent />} placement="bottom-end">
<IconButton>
<MoreVerticalIcon />
</IconButton>
</Menu>
</>
);
const HeaderRightItems: Record<HeaderRightItemNames, ReactNode> = {
editorOptionMenu: <EditorOptionMenu />,
trashButtonGroup: <TrashButtonGroup />,
themeModeSwitch: <ThemeModeSwitch />,
syncUser: <SyncUser />,
};
export const Header = ({ children }: PropsWithChildren<{}>) => {
export const Header = ({
rightItems = ['syncUser'],
children,
}: PropsWithChildren<{ rightItems?: HeaderRightItemNames[] }>) => {
const [showWarning, setShowWarning] = useState(shouldShowWarning());
return (
<StyledHeaderContainer hasWarning={showWarning}>
@@ -197,7 +59,9 @@ export const Header = ({ children }: PropsWithChildren<{}>) => {
<StyledHeader hasWarning={showWarning}>
{children}
<StyledHeaderRightSide>
<HeaderRight />
{rightItems.map(itemName => {
return HeaderRightItems[itemName];
})}
</StyledHeaderRightSide>
</StyledHeader>
</StyledHeaderContainer>