mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-02-13 04:48:53 +00: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 = {
|
||||
workspace: Workspace;
|
||||
page: Page;
|
||||
isJournal?: boolean;
|
||||
};
|
||||
|
||||
export const SharePageButton = ({ workspace, page }: SharePageModalProps) => {
|
||||
export const SharePageButton = ({
|
||||
workspace,
|
||||
page,
|
||||
isJournal,
|
||||
}: SharePageModalProps) => {
|
||||
const [open, setOpen] = useState(false);
|
||||
|
||||
const { openPage } = useNavigateHelper();
|
||||
@@ -35,6 +40,7 @@ export const SharePageButton = ({ workspace, page }: SharePageModalProps) => {
|
||||
return (
|
||||
<>
|
||||
<ShareMenu
|
||||
isJournal={isJournal}
|
||||
workspaceMetadata={workspace.meta}
|
||||
currentPage={page}
|
||||
onEnableAffineCloud={() => setOpen(true)}
|
||||
|
||||
@@ -157,3 +157,8 @@ globalStyle(`${shareLinkStyle} > span`, {
|
||||
globalStyle(`${shareLinkStyle} > div > svg`, {
|
||||
color: 'var(--affine-link-color)',
|
||||
});
|
||||
|
||||
export const journalShareButton = style({
|
||||
height: 32,
|
||||
padding: '0px 8px',
|
||||
});
|
||||
|
||||
@@ -6,6 +6,7 @@ import { useAFFiNEI18N } from '@affine/i18n/hooks';
|
||||
import type { WorkspaceMetadata } from '@affine/workspace';
|
||||
import { WebIcon } from '@blocksuite/icons';
|
||||
import type { Page } from '@blocksuite/store';
|
||||
import clsx from 'clsx';
|
||||
|
||||
import { useIsSharedPage } from '../../../../hooks/affine/use-is-shared-page';
|
||||
import * as styles from './index.css';
|
||||
@@ -15,6 +16,7 @@ import { SharePage } from './share-page';
|
||||
export interface ShareMenuProps {
|
||||
workspaceMetadata: WorkspaceMetadata;
|
||||
currentPage: Page;
|
||||
isJournal?: boolean;
|
||||
onEnableAffineCloud: () => void;
|
||||
}
|
||||
|
||||
@@ -50,7 +52,11 @@ const LocalShareMenu = (props: ShareMenuProps) => {
|
||||
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']()}
|
||||
</Button>
|
||||
</Menu>
|
||||
@@ -76,7 +82,11 @@ const CloudShareMenu = (props: ShareMenuProps) => {
|
||||
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
|
||||
? t['com.affine.share-menu.sharedButton']()
|
||||
: 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';
|
||||
@@ -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',
|
||||
});
|
||||
@@ -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} />;
|
||||
};
|
||||
@@ -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}
|
||||
/>
|
||||
);
|
||||
};
|
||||
@@ -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>
|
||||
);
|
||||
};
|
||||
@@ -4,13 +4,15 @@ import {
|
||||
MenuItem,
|
||||
MenuSeparator,
|
||||
} from '@affine/component/ui/menu';
|
||||
import {
|
||||
Export,
|
||||
FavoriteTag,
|
||||
MoveToTrash,
|
||||
} from '@affine/core/components/page-list';
|
||||
import { currentModeAtom } from '@affine/core/atoms/mode';
|
||||
import { PageHistoryModal } from '@affine/core/components/affine/page-history-modal';
|
||||
import { Export, MoveToTrash } from '@affine/core/components/page-list';
|
||||
import { useBlockSuiteMetaHelper } from '@affine/core/hooks/affine/use-block-suite-meta-helper';
|
||||
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 { waitForCurrentWorkspaceAtom } from '@affine/core/modules/workspace';
|
||||
import { toast } from '@affine/core/utils';
|
||||
import { WorkspaceFlavour } from '@affine/env/workspace';
|
||||
import { useAFFiNEI18N } from '@affine/i18n/hooks';
|
||||
import { assertExists } from '@blocksuite/global/utils';
|
||||
@@ -27,21 +29,21 @@ import {
|
||||
import { useAtomValue } from 'jotai';
|
||||
import { useCallback, useState } from 'react';
|
||||
|
||||
import { currentModeAtom } from '../../../atoms/mode';
|
||||
import { useBlockSuiteMetaHelper } from '../../../hooks/affine/use-block-suite-meta-helper';
|
||||
import { useExportPage } from '../../../hooks/affine/use-export-page';
|
||||
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';
|
||||
import { HeaderDropDownButton } from '../../../pure/header-drop-down-button';
|
||||
import { usePageHelper } from '../../block-suite-page-list/utils';
|
||||
import { useFavorite } from '../favorite';
|
||||
|
||||
type PageMenuProps = {
|
||||
rename?: () => void;
|
||||
pageId: string;
|
||||
isJournal?: boolean;
|
||||
};
|
||||
// fixme: refactor this file
|
||||
export const PageHeaderMenuButton = ({ rename, pageId }: PageMenuProps) => {
|
||||
export const PageHeaderMenuButton = ({
|
||||
rename,
|
||||
pageId,
|
||||
isJournal,
|
||||
}: PageMenuProps) => {
|
||||
const t = useAFFiNEI18N();
|
||||
|
||||
// fixme(himself65): remove these hooks ASAP
|
||||
@@ -54,9 +56,10 @@ export const PageHeaderMenuButton = ({ rename, pageId }: PageMenuProps) => {
|
||||
meta => meta.id === pageId
|
||||
);
|
||||
const currentMode = useAtomValue(currentModeAtom);
|
||||
const favorite = pageMeta?.favorite ?? false;
|
||||
|
||||
const { togglePageMode, toggleFavorite, duplicate } =
|
||||
const { favorite, toggleFavorite } = useFavorite(pageId);
|
||||
|
||||
const { togglePageMode, duplicate } =
|
||||
useBlockSuiteMetaHelper(blockSuiteWorkspace);
|
||||
const { importFile } = usePageHelper(blockSuiteWorkspace);
|
||||
const { setTrashModal } = useTrashModalHelper(blockSuiteWorkspace);
|
||||
@@ -78,14 +81,6 @@ export const PageHeaderMenuButton = ({ rename, pageId }: PageMenuProps) => {
|
||||
});
|
||||
}, [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(() => {
|
||||
togglePageMode(pageId);
|
||||
toast(
|
||||
@@ -107,18 +102,20 @@ export const PageHeaderMenuButton = ({ rename, pageId }: PageMenuProps) => {
|
||||
|
||||
const EditMenu = (
|
||||
<>
|
||||
<MenuItem
|
||||
preFix={
|
||||
<MenuIcon>
|
||||
<EditIcon />
|
||||
</MenuIcon>
|
||||
}
|
||||
data-testid="editor-option-menu-rename"
|
||||
onSelect={rename}
|
||||
style={menuItemStyle}
|
||||
>
|
||||
{t['Rename']()}
|
||||
</MenuItem>
|
||||
{!isJournal && (
|
||||
<MenuItem
|
||||
preFix={
|
||||
<MenuIcon>
|
||||
<EditIcon />
|
||||
</MenuIcon>
|
||||
}
|
||||
data-testid="editor-option-menu-rename"
|
||||
onSelect={rename}
|
||||
style={menuItemStyle}
|
||||
>
|
||||
{t['Rename']()}
|
||||
</MenuItem>
|
||||
)}
|
||||
<MenuItem
|
||||
preFix={
|
||||
<MenuIcon>
|
||||
@@ -136,7 +133,7 @@ export const PageHeaderMenuButton = ({ rename, pageId }: PageMenuProps) => {
|
||||
</MenuItem>
|
||||
<MenuItem
|
||||
data-testid="editor-option-menu-favorite"
|
||||
onSelect={handleFavorite}
|
||||
onSelect={toggleFavorite}
|
||||
style={menuItemStyle}
|
||||
preFix={
|
||||
<MenuIcon>
|
||||
@@ -162,18 +159,20 @@ export const PageHeaderMenuButton = ({ rename, pageId }: PageMenuProps) => {
|
||||
{t['com.affine.header.option.add-tag']()}
|
||||
</MenuItem> */}
|
||||
<MenuSeparator />
|
||||
<MenuItem
|
||||
preFix={
|
||||
<MenuIcon>
|
||||
<DuplicateIcon />
|
||||
</MenuIcon>
|
||||
}
|
||||
data-testid="editor-option-menu-duplicate"
|
||||
onSelect={handleDuplicate}
|
||||
style={menuItemStyle}
|
||||
>
|
||||
{t['com.affine.header.option.duplicate']()}
|
||||
</MenuItem>
|
||||
{!isJournal && (
|
||||
<MenuItem
|
||||
preFix={
|
||||
<MenuIcon>
|
||||
<DuplicateIcon />
|
||||
</MenuIcon>
|
||||
}
|
||||
data-testid="editor-option-menu-duplicate"
|
||||
onSelect={handleDuplicate}
|
||||
style={menuItemStyle}
|
||||
>
|
||||
{t['com.affine.header.option.duplicate']()}
|
||||
</MenuItem>
|
||||
)}
|
||||
<MenuItem
|
||||
preFix={
|
||||
<MenuIcon>
|
||||
@@ -232,7 +231,6 @@ export const PageHeaderMenuButton = ({ rename, pageId }: PageMenuProps) => {
|
||||
onOpenChange={setHistoryModalOpen}
|
||||
/>
|
||||
) : 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',
|
||||
width: '1px',
|
||||
background: 'var(--affine-border-color)',
|
||||
margin: '0 12px',
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user