mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-08-23 13:02:21 +08:00
feat(core): journal hooks and page header layout (#5549)
feat(core): split page header items feat(core): journal page judgment and header layout feat(core): Add journal today button and style changes to share menu
This commit is contained in:
@@ -13,9 +13,14 @@ import { ShareMenu } from './share-menu';
|
|||||||
type SharePageModalProps = {
|
type SharePageModalProps = {
|
||||||
workspace: Workspace;
|
workspace: Workspace;
|
||||||
page: Page;
|
page: Page;
|
||||||
|
isJournal?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const SharePageButton = ({ workspace, page }: SharePageModalProps) => {
|
export const SharePageButton = ({
|
||||||
|
workspace,
|
||||||
|
page,
|
||||||
|
isJournal,
|
||||||
|
}: SharePageModalProps) => {
|
||||||
const [open, setOpen] = useState(false);
|
const [open, setOpen] = useState(false);
|
||||||
|
|
||||||
const { openPage } = useNavigateHelper();
|
const { openPage } = useNavigateHelper();
|
||||||
@@ -35,6 +40,7 @@ export const SharePageButton = ({ workspace, page }: SharePageModalProps) => {
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<ShareMenu
|
<ShareMenu
|
||||||
|
isJournal={isJournal}
|
||||||
workspaceMetadata={workspace.meta}
|
workspaceMetadata={workspace.meta}
|
||||||
currentPage={page}
|
currentPage={page}
|
||||||
onEnableAffineCloud={() => setOpen(true)}
|
onEnableAffineCloud={() => setOpen(true)}
|
||||||
|
|||||||
@@ -157,3 +157,8 @@ globalStyle(`${shareLinkStyle} > span`, {
|
|||||||
globalStyle(`${shareLinkStyle} > div > svg`, {
|
globalStyle(`${shareLinkStyle} > div > svg`, {
|
||||||
color: 'var(--affine-link-color)',
|
color: 'var(--affine-link-color)',
|
||||||
});
|
});
|
||||||
|
|
||||||
|
export const journalShareButton = style({
|
||||||
|
height: 32,
|
||||||
|
padding: '0px 8px',
|
||||||
|
});
|
||||||
|
|||||||
+12
-2
@@ -6,6 +6,7 @@ import { useAFFiNEI18N } from '@affine/i18n/hooks';
|
|||||||
import type { WorkspaceMetadata } from '@affine/workspace';
|
import type { WorkspaceMetadata } from '@affine/workspace';
|
||||||
import { WebIcon } from '@blocksuite/icons';
|
import { WebIcon } from '@blocksuite/icons';
|
||||||
import type { Page } from '@blocksuite/store';
|
import type { Page } from '@blocksuite/store';
|
||||||
|
import clsx from 'clsx';
|
||||||
|
|
||||||
import { useIsSharedPage } from '../../../../hooks/affine/use-is-shared-page';
|
import { useIsSharedPage } from '../../../../hooks/affine/use-is-shared-page';
|
||||||
import * as styles from './index.css';
|
import * as styles from './index.css';
|
||||||
@@ -15,6 +16,7 @@ import { SharePage } from './share-page';
|
|||||||
export interface ShareMenuProps {
|
export interface ShareMenuProps {
|
||||||
workspaceMetadata: WorkspaceMetadata;
|
workspaceMetadata: WorkspaceMetadata;
|
||||||
currentPage: Page;
|
currentPage: Page;
|
||||||
|
isJournal?: boolean;
|
||||||
onEnableAffineCloud: () => void;
|
onEnableAffineCloud: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -50,7 +52,11 @@ const LocalShareMenu = (props: ShareMenuProps) => {
|
|||||||
modal: false,
|
modal: false,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Button data-testid="local-share-menu-button" type="primary">
|
<Button
|
||||||
|
className={clsx({ [styles.journalShareButton]: props.isJournal })}
|
||||||
|
data-testid="local-share-menu-button"
|
||||||
|
type="primary"
|
||||||
|
>
|
||||||
{t['com.affine.share-menu.shareButton']()}
|
{t['com.affine.share-menu.shareButton']()}
|
||||||
</Button>
|
</Button>
|
||||||
</Menu>
|
</Menu>
|
||||||
@@ -76,7 +82,11 @@ const CloudShareMenu = (props: ShareMenuProps) => {
|
|||||||
modal: false,
|
modal: false,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Button data-testid="cloud-share-menu-button" type="primary">
|
<Button
|
||||||
|
className={clsx({ [styles.journalShareButton]: props.isJournal })}
|
||||||
|
data-testid="cloud-share-menu-button"
|
||||||
|
type="primary"
|
||||||
|
>
|
||||||
{isSharedPage
|
{isSharedPage
|
||||||
? t['com.affine.share-menu.sharedButton']()
|
? t['com.affine.share-menu.sharedButton']()
|
||||||
: t['com.affine.share-menu.shareButton']()}
|
: t['com.affine.share-menu.shareButton']()}
|
||||||
|
|||||||
@@ -1,159 +0,0 @@
|
|||||||
import {
|
|
||||||
useBlockSuitePageMeta,
|
|
||||||
usePageMetaHelper,
|
|
||||||
} from '@affine/core/hooks/use-block-suite-page-meta';
|
|
||||||
import type { Workspace as BlockSuiteWorkspace } from '@blocksuite/store';
|
|
||||||
import {
|
|
||||||
type FocusEvent,
|
|
||||||
type InputHTMLAttributes,
|
|
||||||
type KeyboardEvent,
|
|
||||||
useCallback,
|
|
||||||
useEffect,
|
|
||||||
useState,
|
|
||||||
} from 'react';
|
|
||||||
|
|
||||||
import type { PageMode } from '../../../atoms';
|
|
||||||
import { EditorModeSwitch } from '../block-suite-mode-switch';
|
|
||||||
import { PageHeaderMenuButton } from './operation-menu';
|
|
||||||
import * as styles from './styles.css';
|
|
||||||
|
|
||||||
export interface BlockSuiteHeaderTitleProps {
|
|
||||||
blockSuiteWorkspace: BlockSuiteWorkspace;
|
|
||||||
pageId: string;
|
|
||||||
isPublic?: boolean;
|
|
||||||
publicMode?: PageMode;
|
|
||||||
}
|
|
||||||
|
|
||||||
const EditableTitle = ({
|
|
||||||
value,
|
|
||||||
onFocus: propsOnFocus,
|
|
||||||
...inputProps
|
|
||||||
}: InputHTMLAttributes<HTMLInputElement>) => {
|
|
||||||
const onFocus = useCallback(
|
|
||||||
(e: FocusEvent<HTMLInputElement>) => {
|
|
||||||
e.target.select();
|
|
||||||
propsOnFocus?.(e);
|
|
||||||
},
|
|
||||||
[propsOnFocus]
|
|
||||||
);
|
|
||||||
return (
|
|
||||||
<div className={styles.headerTitleContainer}>
|
|
||||||
<input
|
|
||||||
className={styles.titleInput}
|
|
||||||
autoFocus={true}
|
|
||||||
value={value}
|
|
||||||
type="text"
|
|
||||||
data-testid="title-content"
|
|
||||||
onFocus={onFocus}
|
|
||||||
{...inputProps}
|
|
||||||
/>
|
|
||||||
<span className={styles.shadowTitle}>{value}</span>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
const StableTitle = ({
|
|
||||||
blockSuiteWorkspace: workspace,
|
|
||||||
pageId,
|
|
||||||
onRename,
|
|
||||||
isPublic,
|
|
||||||
publicMode,
|
|
||||||
}: BlockSuiteHeaderTitleProps & {
|
|
||||||
onRename?: () => void;
|
|
||||||
}) => {
|
|
||||||
const currentPage = workspace.getPage(pageId);
|
|
||||||
const pageMeta = useBlockSuitePageMeta(workspace).find(
|
|
||||||
meta => meta.id === currentPage?.id
|
|
||||||
);
|
|
||||||
|
|
||||||
const title = pageMeta?.title;
|
|
||||||
|
|
||||||
const handleRename = useCallback(() => {
|
|
||||||
if (!isPublic && onRename) {
|
|
||||||
onRename();
|
|
||||||
}
|
|
||||||
}, [isPublic, onRename]);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className={styles.headerTitleContainer}>
|
|
||||||
<EditorModeSwitch
|
|
||||||
blockSuiteWorkspace={workspace}
|
|
||||||
pageId={pageId}
|
|
||||||
isPublic={isPublic}
|
|
||||||
publicMode={publicMode}
|
|
||||||
/>
|
|
||||||
<span
|
|
||||||
data-testid="title-edit-button"
|
|
||||||
className={styles.titleEditButton}
|
|
||||||
onDoubleClick={handleRename}
|
|
||||||
>
|
|
||||||
{title || 'Untitled'}
|
|
||||||
</span>
|
|
||||||
{isPublic ? null : (
|
|
||||||
<PageHeaderMenuButton rename={onRename} pageId={pageId} />
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
const BlockSuiteTitleWithRename = (props: BlockSuiteHeaderTitleProps) => {
|
|
||||||
const { blockSuiteWorkspace: workspace, pageId } = props;
|
|
||||||
const currentPage = workspace.getPage(pageId);
|
|
||||||
const pageMeta = useBlockSuitePageMeta(workspace).find(
|
|
||||||
meta => meta.id === currentPage?.id
|
|
||||||
);
|
|
||||||
const pageTitleMeta = usePageMetaHelper(workspace);
|
|
||||||
|
|
||||||
const [isEditable, setIsEditable] = useState(false);
|
|
||||||
const [title, setPageTitle] = useState(pageMeta?.title || 'Untitled');
|
|
||||||
|
|
||||||
const onRename = useCallback(() => {
|
|
||||||
setIsEditable(true);
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
const onBlur = useCallback(() => {
|
|
||||||
setIsEditable(false);
|
|
||||||
if (!currentPage?.id) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
pageTitleMeta.setPageTitle(currentPage.id, title);
|
|
||||||
}, [currentPage?.id, pageTitleMeta, title]);
|
|
||||||
|
|
||||||
const handleKeyDown = useCallback(
|
|
||||||
(e: KeyboardEvent<HTMLInputElement>) => {
|
|
||||||
if (e.key === 'Enter' || e.key === 'Escape') {
|
|
||||||
onBlur();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
[onBlur]
|
|
||||||
);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
setPageTitle(pageMeta?.title || '');
|
|
||||||
}, [pageMeta?.title]);
|
|
||||||
|
|
||||||
if (isEditable) {
|
|
||||||
return (
|
|
||||||
<EditableTitle
|
|
||||||
onBlur={onBlur}
|
|
||||||
value={title}
|
|
||||||
onKeyDown={handleKeyDown}
|
|
||||||
onChange={e => {
|
|
||||||
const value = e.target.value;
|
|
||||||
setPageTitle(value);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return <StableTitle {...props} onRename={onRename} />;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const BlockSuiteHeaderTitle = (props: BlockSuiteHeaderTitleProps) => {
|
|
||||||
if (props.isPublic) {
|
|
||||||
return <StableTitle {...props} />;
|
|
||||||
}
|
|
||||||
return <BlockSuiteTitleWithRename {...props} />;
|
|
||||||
};
|
|
||||||
|
|
||||||
BlockSuiteHeaderTitle.displayName = 'BlockSuiteHeaderTitle';
|
|
||||||
-43
@@ -1,43 +0,0 @@
|
|||||||
import { type ComplexStyleRule, style } from '@vanilla-extract/css';
|
|
||||||
|
|
||||||
export const headerTitleContainer = style({
|
|
||||||
display: 'flex',
|
|
||||||
justifyContent: 'flex-start',
|
|
||||||
alignItems: 'center',
|
|
||||||
flexGrow: 1,
|
|
||||||
position: 'relative',
|
|
||||||
overflow: 'hidden',
|
|
||||||
columnGap: 12,
|
|
||||||
});
|
|
||||||
|
|
||||||
export const titleEditButton = style({
|
|
||||||
overflow: 'hidden',
|
|
||||||
textOverflow: 'ellipsis',
|
|
||||||
whiteSpace: 'nowrap',
|
|
||||||
WebkitAppRegion: 'no-drag',
|
|
||||||
} as ComplexStyleRule);
|
|
||||||
|
|
||||||
export const titleInput = style({
|
|
||||||
position: 'absolute',
|
|
||||||
left: 0,
|
|
||||||
right: 0,
|
|
||||||
top: 0,
|
|
||||||
bottom: 0,
|
|
||||||
margin: 'auto',
|
|
||||||
width: '100%',
|
|
||||||
height: '100%',
|
|
||||||
|
|
||||||
selectors: {
|
|
||||||
'&:focus': {
|
|
||||||
border: '1px solid var(--affine-black-10)',
|
|
||||||
borderRadius: '8px',
|
|
||||||
height: '32px',
|
|
||||||
padding: '6px 8px',
|
|
||||||
borderColor: 'var(--affine-primary-color)',
|
|
||||||
boxShadow: 'var(--affine-active-shadow)',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
export const shadowTitle = style({
|
|
||||||
visibility: 'hidden',
|
|
||||||
});
|
|
||||||
+46
@@ -0,0 +1,46 @@
|
|||||||
|
import { FavoriteTag } from '@affine/core/components/page-list';
|
||||||
|
import { useBlockSuiteMetaHelper } from '@affine/core/hooks/affine/use-block-suite-meta-helper';
|
||||||
|
import { useBlockSuitePageMeta } from '@affine/core/hooks/use-block-suite-page-meta';
|
||||||
|
import { waitForCurrentWorkspaceAtom } from '@affine/core/modules/workspace';
|
||||||
|
import { toast } from '@affine/core/utils';
|
||||||
|
import { useAFFiNEI18N } from '@affine/i18n/hooks';
|
||||||
|
import { assertExists } from '@blocksuite/global/utils';
|
||||||
|
import { useAtomValue } from 'jotai';
|
||||||
|
import { useCallback } from 'react';
|
||||||
|
|
||||||
|
export interface FavoriteButtonProps {
|
||||||
|
pageId: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const useFavorite = (pageId: string) => {
|
||||||
|
const t = useAFFiNEI18N();
|
||||||
|
const workspace = useAtomValue(waitForCurrentWorkspaceAtom);
|
||||||
|
const blockSuiteWorkspace = workspace.blockSuiteWorkspace;
|
||||||
|
const currentPage = blockSuiteWorkspace.getPage(pageId);
|
||||||
|
assertExists(currentPage);
|
||||||
|
|
||||||
|
const pageMeta = useBlockSuitePageMeta(blockSuiteWorkspace).find(
|
||||||
|
meta => meta.id === pageId
|
||||||
|
);
|
||||||
|
const favorite = pageMeta?.favorite ?? false;
|
||||||
|
|
||||||
|
const { toggleFavorite: _toggleFavorite } =
|
||||||
|
useBlockSuiteMetaHelper(blockSuiteWorkspace);
|
||||||
|
|
||||||
|
const toggleFavorite = useCallback(() => {
|
||||||
|
_toggleFavorite(pageId);
|
||||||
|
toast(
|
||||||
|
favorite
|
||||||
|
? t['com.affine.toastMessage.removedFavorites']()
|
||||||
|
: t['com.affine.toastMessage.addedFavorites']()
|
||||||
|
);
|
||||||
|
}, [favorite, pageId, t, _toggleFavorite]);
|
||||||
|
|
||||||
|
return { favorite, toggleFavorite };
|
||||||
|
};
|
||||||
|
|
||||||
|
export const FavoriteButton = ({ pageId }: FavoriteButtonProps) => {
|
||||||
|
const { favorite, toggleFavorite } = useFavorite(pageId);
|
||||||
|
|
||||||
|
return <FavoriteTag active={!!favorite} onClick={toggleFavorite} />;
|
||||||
|
};
|
||||||
+42
@@ -0,0 +1,42 @@
|
|||||||
|
import { WeekDatePicker, type WeekDatePickerHandle } from '@affine/component';
|
||||||
|
import {
|
||||||
|
useJournalHelper,
|
||||||
|
useJournalInfoHelper,
|
||||||
|
} from '@affine/core/hooks/use-journal';
|
||||||
|
import type { BlockSuiteWorkspace } from '@affine/core/shared';
|
||||||
|
import type { Page } from '@blocksuite/store';
|
||||||
|
import dayjs from 'dayjs';
|
||||||
|
import { useEffect, useRef, useState } from 'react';
|
||||||
|
|
||||||
|
export interface JournalWeekDatePickerProps {
|
||||||
|
workspace: BlockSuiteWorkspace;
|
||||||
|
page: Page;
|
||||||
|
}
|
||||||
|
|
||||||
|
const weekStyle = { maxWidth: 548, width: '100%' };
|
||||||
|
export const JournalWeekDatePicker = ({
|
||||||
|
workspace,
|
||||||
|
page,
|
||||||
|
}: JournalWeekDatePickerProps) => {
|
||||||
|
const handleRef = useRef<WeekDatePickerHandle>(null);
|
||||||
|
const { journalDate } = useJournalInfoHelper(page.meta);
|
||||||
|
const { openJournal } = useJournalHelper(workspace);
|
||||||
|
const [date, setDate] = useState(
|
||||||
|
(journalDate ?? dayjs()).format('YYYY-MM-DD')
|
||||||
|
);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!journalDate) return;
|
||||||
|
setDate(journalDate.format('YYYY-MM-DD'));
|
||||||
|
handleRef.current?.setCursor?.(journalDate);
|
||||||
|
}, [journalDate]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<WeekDatePicker
|
||||||
|
handleRef={handleRef}
|
||||||
|
style={weekStyle}
|
||||||
|
value={date}
|
||||||
|
onChange={openJournal}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
+28
@@ -0,0 +1,28 @@
|
|||||||
|
import { Button } from '@affine/component';
|
||||||
|
import { useJournalHelper } from '@affine/core/hooks/use-journal';
|
||||||
|
import type { BlockSuiteWorkspace } from '@affine/core/shared';
|
||||||
|
import { useAFFiNEI18N } from '@affine/i18n/hooks';
|
||||||
|
import { useCallback } from 'react';
|
||||||
|
|
||||||
|
export interface JournalTodayButtonProps {
|
||||||
|
workspace: BlockSuiteWorkspace;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const JournalTodayButton = ({ workspace }: JournalTodayButtonProps) => {
|
||||||
|
const t = useAFFiNEI18N();
|
||||||
|
const journalHelper = useJournalHelper(workspace);
|
||||||
|
|
||||||
|
const onToday = useCallback(() => {
|
||||||
|
journalHelper.openToday();
|
||||||
|
}, [journalHelper]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Button
|
||||||
|
size="default"
|
||||||
|
onClick={onToday}
|
||||||
|
style={{ height: 32, padding: '0px 8px' }}
|
||||||
|
>
|
||||||
|
{t['com.affine.today']()}
|
||||||
|
</Button>
|
||||||
|
);
|
||||||
|
};
|
||||||
+48
-50
@@ -4,13 +4,15 @@ import {
|
|||||||
MenuItem,
|
MenuItem,
|
||||||
MenuSeparator,
|
MenuSeparator,
|
||||||
} from '@affine/component/ui/menu';
|
} from '@affine/component/ui/menu';
|
||||||
import {
|
import { currentModeAtom } from '@affine/core/atoms/mode';
|
||||||
Export,
|
import { PageHistoryModal } from '@affine/core/components/affine/page-history-modal';
|
||||||
FavoriteTag,
|
import { Export, MoveToTrash } from '@affine/core/components/page-list';
|
||||||
MoveToTrash,
|
import { useBlockSuiteMetaHelper } from '@affine/core/hooks/affine/use-block-suite-meta-helper';
|
||||||
} from '@affine/core/components/page-list';
|
import { useExportPage } from '@affine/core/hooks/affine/use-export-page';
|
||||||
|
import { useTrashModalHelper } from '@affine/core/hooks/affine/use-trash-modal-helper';
|
||||||
import { useBlockSuitePageMeta } from '@affine/core/hooks/use-block-suite-page-meta';
|
import { useBlockSuitePageMeta } from '@affine/core/hooks/use-block-suite-page-meta';
|
||||||
import { waitForCurrentWorkspaceAtom } from '@affine/core/modules/workspace';
|
import { waitForCurrentWorkspaceAtom } from '@affine/core/modules/workspace';
|
||||||
|
import { toast } from '@affine/core/utils';
|
||||||
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 { assertExists } from '@blocksuite/global/utils';
|
import { assertExists } from '@blocksuite/global/utils';
|
||||||
@@ -27,21 +29,21 @@ import {
|
|||||||
import { useAtomValue } from 'jotai';
|
import { useAtomValue } from 'jotai';
|
||||||
import { useCallback, useState } from 'react';
|
import { useCallback, useState } from 'react';
|
||||||
|
|
||||||
import { currentModeAtom } from '../../../atoms/mode';
|
import { HeaderDropDownButton } from '../../../pure/header-drop-down-button';
|
||||||
import { useBlockSuiteMetaHelper } from '../../../hooks/affine/use-block-suite-meta-helper';
|
import { usePageHelper } from '../../block-suite-page-list/utils';
|
||||||
import { useExportPage } from '../../../hooks/affine/use-export-page';
|
import { useFavorite } from '../favorite';
|
||||||
import { useTrashModalHelper } from '../../../hooks/affine/use-trash-modal-helper';
|
|
||||||
import { toast } from '../../../utils';
|
|
||||||
import { PageHistoryModal } from '../../affine/page-history-modal/history-modal';
|
|
||||||
import { HeaderDropDownButton } from '../../pure/header-drop-down-button';
|
|
||||||
import { usePageHelper } from '../block-suite-page-list/utils';
|
|
||||||
|
|
||||||
type PageMenuProps = {
|
type PageMenuProps = {
|
||||||
rename?: () => void;
|
rename?: () => void;
|
||||||
pageId: string;
|
pageId: string;
|
||||||
|
isJournal?: boolean;
|
||||||
};
|
};
|
||||||
// fixme: refactor this file
|
// fixme: refactor this file
|
||||||
export const PageHeaderMenuButton = ({ rename, pageId }: PageMenuProps) => {
|
export const PageHeaderMenuButton = ({
|
||||||
|
rename,
|
||||||
|
pageId,
|
||||||
|
isJournal,
|
||||||
|
}: PageMenuProps) => {
|
||||||
const t = useAFFiNEI18N();
|
const t = useAFFiNEI18N();
|
||||||
|
|
||||||
// fixme(himself65): remove these hooks ASAP
|
// fixme(himself65): remove these hooks ASAP
|
||||||
@@ -54,9 +56,10 @@ export const PageHeaderMenuButton = ({ rename, pageId }: PageMenuProps) => {
|
|||||||
meta => meta.id === pageId
|
meta => meta.id === pageId
|
||||||
);
|
);
|
||||||
const currentMode = useAtomValue(currentModeAtom);
|
const currentMode = useAtomValue(currentModeAtom);
|
||||||
const favorite = pageMeta?.favorite ?? false;
|
|
||||||
|
|
||||||
const { togglePageMode, toggleFavorite, duplicate } =
|
const { favorite, toggleFavorite } = useFavorite(pageId);
|
||||||
|
|
||||||
|
const { togglePageMode, duplicate } =
|
||||||
useBlockSuiteMetaHelper(blockSuiteWorkspace);
|
useBlockSuiteMetaHelper(blockSuiteWorkspace);
|
||||||
const { importFile } = usePageHelper(blockSuiteWorkspace);
|
const { importFile } = usePageHelper(blockSuiteWorkspace);
|
||||||
const { setTrashModal } = useTrashModalHelper(blockSuiteWorkspace);
|
const { setTrashModal } = useTrashModalHelper(blockSuiteWorkspace);
|
||||||
@@ -78,14 +81,6 @@ export const PageHeaderMenuButton = ({ rename, pageId }: PageMenuProps) => {
|
|||||||
});
|
});
|
||||||
}, [pageId, pageMeta, setTrashModal]);
|
}, [pageId, pageMeta, setTrashModal]);
|
||||||
|
|
||||||
const handleFavorite = useCallback(() => {
|
|
||||||
toggleFavorite(pageId);
|
|
||||||
toast(
|
|
||||||
favorite
|
|
||||||
? t['com.affine.toastMessage.removedFavorites']()
|
|
||||||
: t['com.affine.toastMessage.addedFavorites']()
|
|
||||||
);
|
|
||||||
}, [favorite, pageId, t, toggleFavorite]);
|
|
||||||
const handleSwitchMode = useCallback(() => {
|
const handleSwitchMode = useCallback(() => {
|
||||||
togglePageMode(pageId);
|
togglePageMode(pageId);
|
||||||
toast(
|
toast(
|
||||||
@@ -107,18 +102,20 @@ export const PageHeaderMenuButton = ({ rename, pageId }: PageMenuProps) => {
|
|||||||
|
|
||||||
const EditMenu = (
|
const EditMenu = (
|
||||||
<>
|
<>
|
||||||
<MenuItem
|
{!isJournal && (
|
||||||
preFix={
|
<MenuItem
|
||||||
<MenuIcon>
|
preFix={
|
||||||
<EditIcon />
|
<MenuIcon>
|
||||||
</MenuIcon>
|
<EditIcon />
|
||||||
}
|
</MenuIcon>
|
||||||
data-testid="editor-option-menu-rename"
|
}
|
||||||
onSelect={rename}
|
data-testid="editor-option-menu-rename"
|
||||||
style={menuItemStyle}
|
onSelect={rename}
|
||||||
>
|
style={menuItemStyle}
|
||||||
{t['Rename']()}
|
>
|
||||||
</MenuItem>
|
{t['Rename']()}
|
||||||
|
</MenuItem>
|
||||||
|
)}
|
||||||
<MenuItem
|
<MenuItem
|
||||||
preFix={
|
preFix={
|
||||||
<MenuIcon>
|
<MenuIcon>
|
||||||
@@ -136,7 +133,7 @@ export const PageHeaderMenuButton = ({ rename, pageId }: PageMenuProps) => {
|
|||||||
</MenuItem>
|
</MenuItem>
|
||||||
<MenuItem
|
<MenuItem
|
||||||
data-testid="editor-option-menu-favorite"
|
data-testid="editor-option-menu-favorite"
|
||||||
onSelect={handleFavorite}
|
onSelect={toggleFavorite}
|
||||||
style={menuItemStyle}
|
style={menuItemStyle}
|
||||||
preFix={
|
preFix={
|
||||||
<MenuIcon>
|
<MenuIcon>
|
||||||
@@ -162,18 +159,20 @@ export const PageHeaderMenuButton = ({ rename, pageId }: PageMenuProps) => {
|
|||||||
{t['com.affine.header.option.add-tag']()}
|
{t['com.affine.header.option.add-tag']()}
|
||||||
</MenuItem> */}
|
</MenuItem> */}
|
||||||
<MenuSeparator />
|
<MenuSeparator />
|
||||||
<MenuItem
|
{!isJournal && (
|
||||||
preFix={
|
<MenuItem
|
||||||
<MenuIcon>
|
preFix={
|
||||||
<DuplicateIcon />
|
<MenuIcon>
|
||||||
</MenuIcon>
|
<DuplicateIcon />
|
||||||
}
|
</MenuIcon>
|
||||||
data-testid="editor-option-menu-duplicate"
|
}
|
||||||
onSelect={handleDuplicate}
|
data-testid="editor-option-menu-duplicate"
|
||||||
style={menuItemStyle}
|
onSelect={handleDuplicate}
|
||||||
>
|
style={menuItemStyle}
|
||||||
{t['com.affine.header.option.duplicate']()}
|
>
|
||||||
</MenuItem>
|
{t['com.affine.header.option.duplicate']()}
|
||||||
|
</MenuItem>
|
||||||
|
)}
|
||||||
<MenuItem
|
<MenuItem
|
||||||
preFix={
|
preFix={
|
||||||
<MenuIcon>
|
<MenuIcon>
|
||||||
@@ -232,7 +231,6 @@ export const PageHeaderMenuButton = ({ rename, pageId }: PageMenuProps) => {
|
|||||||
onOpenChange={setHistoryModalOpen}
|
onOpenChange={setHistoryModalOpen}
|
||||||
/>
|
/>
|
||||||
) : null}
|
) : null}
|
||||||
<FavoriteTag active={!!pageMeta?.favorite} onClick={handleFavorite} />
|
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
@@ -0,0 +1,57 @@
|
|||||||
|
import { InlineEdit, type InlineEditProps } from '@affine/component';
|
||||||
|
import {
|
||||||
|
useBlockSuitePageMeta,
|
||||||
|
usePageMetaHelper,
|
||||||
|
} from '@affine/core/hooks/use-block-suite-page-meta';
|
||||||
|
import type { BlockSuiteWorkspace } from '@affine/core/shared';
|
||||||
|
import type { HTMLAttributes } from 'react';
|
||||||
|
import { useCallback } from 'react';
|
||||||
|
|
||||||
|
import * as styles from './style.css';
|
||||||
|
|
||||||
|
export interface BlockSuiteHeaderTitleProps {
|
||||||
|
blockSuiteWorkspace: BlockSuiteWorkspace;
|
||||||
|
pageId: string;
|
||||||
|
/** if set, title cannot be edited */
|
||||||
|
isPublic?: boolean;
|
||||||
|
inputHandleRef?: InlineEditProps['handleRef'];
|
||||||
|
}
|
||||||
|
|
||||||
|
const inputAttrs = {
|
||||||
|
'data-testid': 'title-content',
|
||||||
|
} as HTMLAttributes<HTMLInputElement>;
|
||||||
|
export const BlocksuiteHeaderTitle = (props: BlockSuiteHeaderTitleProps) => {
|
||||||
|
const {
|
||||||
|
blockSuiteWorkspace: workspace,
|
||||||
|
pageId,
|
||||||
|
isPublic,
|
||||||
|
inputHandleRef,
|
||||||
|
} = props;
|
||||||
|
const currentPage = workspace.getPage(pageId);
|
||||||
|
const pageMeta = useBlockSuitePageMeta(workspace).find(
|
||||||
|
meta => meta.id === currentPage?.id
|
||||||
|
);
|
||||||
|
const title = pageMeta?.title;
|
||||||
|
const { setPageTitle } = usePageMetaHelper(workspace);
|
||||||
|
|
||||||
|
const onChange = useCallback(
|
||||||
|
(v: string) => {
|
||||||
|
setPageTitle(currentPage?.id || '', v);
|
||||||
|
},
|
||||||
|
[currentPage?.id, setPageTitle]
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<InlineEdit
|
||||||
|
className={styles.title}
|
||||||
|
autoSelect
|
||||||
|
value={title}
|
||||||
|
onChange={onChange}
|
||||||
|
editable={!isPublic}
|
||||||
|
placeholder="Untitled"
|
||||||
|
data-testid="title-edit-button"
|
||||||
|
handleRef={inputHandleRef}
|
||||||
|
inputAttrs={inputAttrs}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
import { style } from '@vanilla-extract/css';
|
||||||
|
|
||||||
|
export const title = style({
|
||||||
|
fontWeight: 500,
|
||||||
|
color: 'var(--affine-text-primary-color)',
|
||||||
|
});
|
||||||
@@ -122,5 +122,4 @@ export const headerDivider = style({
|
|||||||
height: '20px',
|
height: '20px',
|
||||||
width: '1px',
|
width: '1px',
|
||||||
background: 'var(--affine-border-color)',
|
background: 'var(--affine-border-color)',
|
||||||
margin: '0 12px',
|
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -0,0 +1,113 @@
|
|||||||
|
import type { PageMeta } from '@blocksuite/store';
|
||||||
|
import { initEmptyPage } from '@toeverything/infra/blocksuite';
|
||||||
|
import dayjs from 'dayjs';
|
||||||
|
import { useCallback, useMemo } from 'react';
|
||||||
|
|
||||||
|
import type { BlockSuiteWorkspace } from '../shared';
|
||||||
|
import { useBlockSuiteWorkspaceHelper } from './use-block-suite-workspace-helper';
|
||||||
|
import { useNavigateHelper } from './use-navigate-helper';
|
||||||
|
|
||||||
|
type MaybeDate = Date | string | number;
|
||||||
|
export const JOURNAL_DATE_FORMAT = 'YYYY-MM-DD';
|
||||||
|
|
||||||
|
function isPageJournal(pageMeta?: PageMeta) {
|
||||||
|
return !!(pageMeta && pageMeta.title.match(/^\d{4}-\d{2}-\d{2}$/));
|
||||||
|
}
|
||||||
|
|
||||||
|
function getJournalDate(pageMeta?: PageMeta) {
|
||||||
|
if (!isPageJournal(pageMeta)) return null;
|
||||||
|
if (!pageMeta?.title) return null;
|
||||||
|
if (!dayjs(pageMeta.title).isValid()) return null;
|
||||||
|
return dayjs(pageMeta.title);
|
||||||
|
}
|
||||||
|
|
||||||
|
export const useJournalHelper = (workspace: BlockSuiteWorkspace) => {
|
||||||
|
const bsWorkspaceHelper = useBlockSuiteWorkspaceHelper(workspace);
|
||||||
|
const navigateHelper = useNavigateHelper();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
const _createJournal = useCallback(
|
||||||
|
(maybeDate: MaybeDate) => {
|
||||||
|
const title = dayjs(maybeDate).format(JOURNAL_DATE_FORMAT);
|
||||||
|
const page = bsWorkspaceHelper.createPage();
|
||||||
|
initEmptyPage(page, title).catch(err =>
|
||||||
|
console.error('Failed to load journal page', err)
|
||||||
|
);
|
||||||
|
return page;
|
||||||
|
},
|
||||||
|
[bsWorkspaceHelper]
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* query all journals by date
|
||||||
|
*/
|
||||||
|
const getJournalsByDate = useCallback(
|
||||||
|
(maybeDate: MaybeDate) => {
|
||||||
|
const day = dayjs(maybeDate);
|
||||||
|
return Array.from(workspace.pages.values()).filter(page => {
|
||||||
|
if (!isPageJournal(page.meta)) return false;
|
||||||
|
if (page.meta.trash) return false;
|
||||||
|
const journalDate = getJournalDate(page.meta);
|
||||||
|
if (!journalDate) return false;
|
||||||
|
return day.isSame(journalDate, 'day');
|
||||||
|
});
|
||||||
|
},
|
||||||
|
[workspace.pages]
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* get journal by date, create one if not exist
|
||||||
|
*/
|
||||||
|
const getJournalByDate = useCallback(
|
||||||
|
(maybeDate: MaybeDate) => {
|
||||||
|
const pages = getJournalsByDate(maybeDate);
|
||||||
|
if (pages.length) return pages[0];
|
||||||
|
return _createJournal(maybeDate);
|
||||||
|
},
|
||||||
|
[_createJournal, getJournalsByDate]
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* open journal by date, create one if not exist
|
||||||
|
*/
|
||||||
|
const openJournal = useCallback(
|
||||||
|
(maybeDate: MaybeDate) => {
|
||||||
|
const page = getJournalByDate(maybeDate);
|
||||||
|
navigateHelper.openPage(workspace.id, page.id);
|
||||||
|
},
|
||||||
|
[getJournalByDate, navigateHelper, workspace.id]
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* open today's journal
|
||||||
|
*/
|
||||||
|
const openToday = useCallback(() => {
|
||||||
|
const date = dayjs().format(JOURNAL_DATE_FORMAT);
|
||||||
|
openJournal(date);
|
||||||
|
}, [openJournal]);
|
||||||
|
|
||||||
|
return useMemo(
|
||||||
|
() => ({
|
||||||
|
getJournalsByDate,
|
||||||
|
getJournalByDate,
|
||||||
|
openJournal,
|
||||||
|
openToday,
|
||||||
|
}),
|
||||||
|
[getJournalByDate, getJournalsByDate, openJournal, openToday]
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useJournalInfoHelper = (pageMeta?: PageMeta) => {
|
||||||
|
const isJournal = isPageJournal(pageMeta);
|
||||||
|
const journalDate = useMemo(() => getJournalDate(pageMeta), [pageMeta]);
|
||||||
|
|
||||||
|
return useMemo(
|
||||||
|
() => ({
|
||||||
|
isJournal,
|
||||||
|
journalDate,
|
||||||
|
}),
|
||||||
|
[isJournal, journalDate]
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -1,7 +1,8 @@
|
|||||||
|
import { EditorModeSwitch } from '@affine/core/components/blocksuite/block-suite-mode-switch';
|
||||||
import type { Workspace as BlockSuiteWorkspace } from '@blocksuite/store';
|
import type { Workspace as BlockSuiteWorkspace } from '@blocksuite/store';
|
||||||
|
|
||||||
import type { PageMode } from '../../atoms';
|
import type { PageMode } from '../../atoms';
|
||||||
import { BlockSuiteHeaderTitle } from '../../components/blocksuite/block-suite-header-title';
|
import { BlocksuiteHeaderTitle } from '../../components/blocksuite/block-suite-header/title/index';
|
||||||
import ShareHeaderLeftItem from '../../components/cloud/share-header-left-item';
|
import ShareHeaderLeftItem from '../../components/cloud/share-header-left-item';
|
||||||
import ShareHeaderRightItem from '../../components/cloud/share-header-right-item';
|
import ShareHeaderRightItem from '../../components/cloud/share-header-right-item';
|
||||||
import { Header } from '../../components/pure/header';
|
import { Header } from '../../components/pure/header';
|
||||||
@@ -20,12 +21,19 @@ export function ShareHeader({
|
|||||||
isFloat={publishMode === 'edgeless'}
|
isFloat={publishMode === 'edgeless'}
|
||||||
left={<ShareHeaderLeftItem />}
|
left={<ShareHeaderLeftItem />}
|
||||||
center={
|
center={
|
||||||
<BlockSuiteHeaderTitle
|
<>
|
||||||
blockSuiteWorkspace={blockSuiteWorkspace}
|
<EditorModeSwitch
|
||||||
pageId={pageId}
|
isPublic
|
||||||
isPublic={true}
|
blockSuiteWorkspace={blockSuiteWorkspace}
|
||||||
publicMode={publishMode}
|
pageId={pageId}
|
||||||
/>
|
publicMode={publishMode}
|
||||||
|
/>
|
||||||
|
<BlocksuiteHeaderTitle
|
||||||
|
blockSuiteWorkspace={blockSuiteWorkspace}
|
||||||
|
pageId={pageId}
|
||||||
|
isPublic={true}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
}
|
}
|
||||||
right={
|
right={
|
||||||
<ShareHeaderRightItem
|
<ShareHeaderRightItem
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ export const mainHeader = style([
|
|||||||
header,
|
header,
|
||||||
{
|
{
|
||||||
padding: '0 16px',
|
padding: '0 16px',
|
||||||
|
gap: 12,
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
|
|
||||||
@@ -58,3 +59,11 @@ export const standaloneExtensionSwitcherWrapper = style({
|
|||||||
height: '52px',
|
height: '52px',
|
||||||
position: 'relative',
|
position: 'relative',
|
||||||
});
|
});
|
||||||
|
|
||||||
|
export const journalWeekPicker = style({
|
||||||
|
minWidth: 100,
|
||||||
|
flexGrow: 1,
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'center',
|
||||||
|
});
|
||||||
|
|||||||
@@ -1,16 +1,24 @@
|
|||||||
|
import type { InlineEditHandle } from '@affine/component';
|
||||||
import { IconButton } from '@affine/component';
|
import { IconButton } from '@affine/component';
|
||||||
import {
|
import {
|
||||||
appSidebarFloatingAtom,
|
appSidebarFloatingAtom,
|
||||||
appSidebarOpenAtom,
|
appSidebarOpenAtom,
|
||||||
SidebarSwitch,
|
SidebarSwitch,
|
||||||
} from '@affine/component/app-sidebar';
|
} from '@affine/component/app-sidebar';
|
||||||
|
import { FavoriteButton } from '@affine/core/components/blocksuite/block-suite-header/favorite';
|
||||||
|
import { JournalWeekDatePicker } from '@affine/core/components/blocksuite/block-suite-header/journal/date-picker';
|
||||||
|
import { JournalTodayButton } from '@affine/core/components/blocksuite/block-suite-header/journal/today-button';
|
||||||
|
import { PageHeaderMenuButton } from '@affine/core/components/blocksuite/block-suite-header/menu';
|
||||||
|
import { EditorModeSwitch } from '@affine/core/components/blocksuite/block-suite-mode-switch';
|
||||||
|
import { useJournalInfoHelper } from '@affine/core/hooks/use-journal';
|
||||||
import type { Workspace } from '@affine/workspace';
|
import type { Workspace } from '@affine/workspace';
|
||||||
import { RightSidebarIcon } from '@blocksuite/icons';
|
import { RightSidebarIcon } from '@blocksuite/icons';
|
||||||
import type { Page } from '@blocksuite/store';
|
import type { Page } from '@blocksuite/store';
|
||||||
import { useAtomValue, useSetAtom } from 'jotai';
|
import { useAtomValue, useSetAtom } from 'jotai';
|
||||||
|
import { useCallback, useRef } from 'react';
|
||||||
|
|
||||||
import { SharePageButton } from '../../../components/affine/share-page-modal';
|
import { SharePageButton } from '../../../components/affine/share-page-modal';
|
||||||
import { BlockSuiteHeaderTitle } from '../../../components/blocksuite/block-suite-header-title';
|
import { BlocksuiteHeaderTitle } from '../../../components/blocksuite/block-suite-header/title/index';
|
||||||
import { HeaderDivider } from '../../../components/pure/header';
|
import { HeaderDivider } from '../../../components/pure/header';
|
||||||
import { WindowsAppControls } from '../../../components/pure/header/windows-app-controls';
|
import { WindowsAppControls } from '../../../components/pure/header/windows-app-controls';
|
||||||
import * as styles from './detail-page-header.css';
|
import * as styles from './detail-page-header.css';
|
||||||
@@ -46,12 +54,7 @@ const WindowsMainPageHeaderRight = ({
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<HeaderDivider />
|
<HeaderDivider />
|
||||||
<div
|
<div className={styles.mainHeaderRight} style={{ marginRight: -16 }}>
|
||||||
className={styles.mainHeaderRight}
|
|
||||||
style={{
|
|
||||||
marginRight: editorSidebarOpen ? 0 : -16,
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{showSidebarSwitch ? <ToggleSidebarButton /> : null}
|
{showSidebarSwitch ? <ToggleSidebarButton /> : null}
|
||||||
<WindowsAppControls />
|
<WindowsAppControls />
|
||||||
</div>
|
</div>
|
||||||
@@ -100,27 +103,71 @@ function Header({
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function DetailPageHeader({
|
interface PageHeaderProps {
|
||||||
page,
|
|
||||||
workspace,
|
|
||||||
showSidebarSwitch = true,
|
|
||||||
}: {
|
|
||||||
page: Page;
|
page: Page;
|
||||||
workspace: Workspace;
|
workspace: Workspace;
|
||||||
showSidebarSwitch?: boolean;
|
showSidebarSwitch?: boolean;
|
||||||
}) {
|
}
|
||||||
|
const RightHeader = isWindowsDesktop
|
||||||
|
? WindowsMainPageHeaderRight
|
||||||
|
: NonWindowsMainPageHeaderRight;
|
||||||
|
export function JournalPageHeader({
|
||||||
|
page,
|
||||||
|
workspace,
|
||||||
|
showSidebarSwitch = true,
|
||||||
|
}: PageHeaderProps) {
|
||||||
const leftSidebarOpen = useAtomValue(appSidebarOpenAtom);
|
const leftSidebarOpen = useAtomValue(appSidebarOpenAtom);
|
||||||
const RightHeader = isWindowsDesktop
|
|
||||||
? WindowsMainPageHeaderRight
|
|
||||||
: NonWindowsMainPageHeaderRight;
|
|
||||||
return (
|
return (
|
||||||
<Header className={styles.mainHeader}>
|
<Header className={styles.mainHeader}>
|
||||||
<SidebarSwitch show={!leftSidebarOpen} />
|
<SidebarSwitch show={!leftSidebarOpen} />
|
||||||
{!leftSidebarOpen ? <HeaderDivider /> : null}
|
{!leftSidebarOpen ? <HeaderDivider /> : null}
|
||||||
<BlockSuiteHeaderTitle
|
<EditorModeSwitch
|
||||||
pageId={page.id}
|
blockSuiteWorkspace={workspace.blockSuiteWorkspace}
|
||||||
|
pageId={page?.id}
|
||||||
|
/>
|
||||||
|
<div className={styles.journalWeekPicker}>
|
||||||
|
<JournalWeekDatePicker
|
||||||
|
workspace={workspace.blockSuiteWorkspace}
|
||||||
|
page={page}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<JournalTodayButton workspace={workspace.blockSuiteWorkspace} />
|
||||||
|
<HeaderDivider />
|
||||||
|
<PageHeaderMenuButton isJournal pageId={page?.id} />
|
||||||
|
{page ? (
|
||||||
|
<SharePageButton isJournal workspace={workspace} page={page} />
|
||||||
|
) : null}
|
||||||
|
<RightHeader showSidebarSwitch={showSidebarSwitch} />
|
||||||
|
</Header>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function NormalPageHeader({
|
||||||
|
page,
|
||||||
|
workspace,
|
||||||
|
showSidebarSwitch = true,
|
||||||
|
}: PageHeaderProps) {
|
||||||
|
const titleInputHandleRef = useRef<InlineEditHandle>(null);
|
||||||
|
const leftSidebarOpen = useAtomValue(appSidebarOpenAtom);
|
||||||
|
|
||||||
|
const onRename = useCallback(() => {
|
||||||
|
setTimeout(() => titleInputHandleRef.current?.triggerEdit());
|
||||||
|
}, []);
|
||||||
|
return (
|
||||||
|
<Header className={styles.mainHeader}>
|
||||||
|
<SidebarSwitch show={!leftSidebarOpen} />
|
||||||
|
{!leftSidebarOpen ? <HeaderDivider /> : null}
|
||||||
|
<EditorModeSwitch
|
||||||
|
blockSuiteWorkspace={workspace.blockSuiteWorkspace}
|
||||||
|
pageId={page?.id}
|
||||||
|
/>
|
||||||
|
<BlocksuiteHeaderTitle
|
||||||
|
inputHandleRef={titleInputHandleRef}
|
||||||
|
pageId={page?.id}
|
||||||
blockSuiteWorkspace={workspace.blockSuiteWorkspace}
|
blockSuiteWorkspace={workspace.blockSuiteWorkspace}
|
||||||
/>
|
/>
|
||||||
|
<PageHeaderMenuButton rename={onRename} pageId={page?.id} />
|
||||||
|
<FavoriteButton pageId={page?.id} />
|
||||||
<div className={styles.spacer} />
|
<div className={styles.spacer} />
|
||||||
{page ? <SharePageButton workspace={workspace} page={page} /> : null}
|
{page ? <SharePageButton workspace={workspace} page={page} /> : null}
|
||||||
<RightHeader showSidebarSwitch={showSidebarSwitch} />
|
<RightHeader showSidebarSwitch={showSidebarSwitch} />
|
||||||
@@ -128,6 +175,18 @@ export function DetailPageHeader({
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function DetailPageHeader(props: PageHeaderProps) {
|
||||||
|
const { page } = props;
|
||||||
|
const { isJournal } = useJournalInfoHelper(page.meta);
|
||||||
|
const isInTrash = page.meta.trash;
|
||||||
|
|
||||||
|
return isJournal && !isInTrash ? (
|
||||||
|
<JournalPageHeader {...props} />
|
||||||
|
) : (
|
||||||
|
<NormalPageHeader {...props} />
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
function WindowsSidebarHeader() {
|
function WindowsSidebarHeader() {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
|||||||
Reference in New Issue
Block a user