feat(mobile): show doc title on title bar when scroll down (#8976)

This commit is contained in:
CatsJuice
2024-12-02 06:00:31 +00:00
parent 84210dd197
commit 9b4cd83a07
4 changed files with 61 additions and 11 deletions

View File

@@ -31,6 +31,7 @@ export interface PageHeaderProps
* @default true * @default true
*/ */
centerContent?: boolean; centerContent?: boolean;
contentClassName?: string;
prefixClassName?: string; prefixClassName?: string;
prefixStyle?: React.CSSProperties; prefixStyle?: React.CSSProperties;
@@ -56,6 +57,7 @@ export const PageHeader = forwardRef<HTMLDivElement, PageHeaderProps>(
children, children,
className, className,
centerContent = true, centerContent = true,
contentClassName,
prefixClassName, prefixClassName,
prefixStyle, prefixStyle,
suffixClassName, suffixClassName,
@@ -85,7 +87,9 @@ export const PageHeader = forwardRef<HTMLDivElement, PageHeaderProps>(
</section> </section>
<section <section
className={clsx(styles.content, { center: centerContent })} className={clsx(styles.content, contentClassName, {
center: centerContent,
})}
> >
{children} {children}
</section> </section>

View File

@@ -1,5 +1,5 @@
import { cssVarV2 } from '@toeverything/theme/v2'; import { cssVarV2 } from '@toeverything/theme/v2';
import { style } from '@vanilla-extract/css'; import { createVar, style } from '@vanilla-extract/css';
export const root = style({ export const root = style({
width: '100%', width: '100%',
@@ -18,14 +18,22 @@ export const inner = style({
alignItems: 'center', alignItems: 'center',
justifyContent: 'space-between', justifyContent: 'space-between',
}); });
const contentMaxWidth = createVar('contentMaxWidth');
export const content = style({ export const content = style({
vars: {
[contentMaxWidth]: 'unset',
},
maxWidth: contentMaxWidth,
selectors: { selectors: {
'&.center': { '&.center': {
vars: {
// gap(6 * 2) + button(44 * 2) + padding(8 * 2)
[contentMaxWidth]: 'calc(100% - 12px - 88px - 16px)',
},
position: 'absolute', position: 'absolute',
left: '50%', left: '50%',
transform: 'translateX(-50%)', transform: 'translateX(-50%)',
width: 'fit-content', width: 'fit-content',
maxWidth: 'calc(100% - 12px - 88px - 16px)',
display: 'flex', display: 'flex',
justifyContent: 'center', justifyContent: 'center',
pointerEvents: 'none', pointerEvents: 'none',

View File

@@ -1,4 +1,5 @@
import { cssVar } from '@toeverything/theme'; import { cssVar } from '@toeverything/theme';
import { bodyEmphasized } from '@toeverything/theme/typography';
import { cssVarV2 } from '@toeverything/theme/v2'; import { cssVarV2 } from '@toeverything/theme/v2';
import { globalStyle, style } from '@vanilla-extract/css'; import { globalStyle, style } from '@vanilla-extract/css';
@@ -16,6 +17,26 @@ export const header = style({
zIndex: 1, zIndex: 1,
}); });
export const headerContent = style({
maxWidth: `calc(100% - 200px)`,
});
export const headerTitle = style([
bodyEmphasized,
{
overflow: 'hidden',
textOverflow: 'ellipsis',
whiteSpace: 'nowrap',
opacity: 0,
transition: 'opacity 0.23s ease',
selectors: {
'&[data-show="true"]': {
opacity: 1,
},
},
},
]);
export const mainContainer = style({ export const mainContainer = style({
containerType: 'inline-size', containerType: 'inline-size',
display: 'flex', display: 'flex',

View File

@@ -10,11 +10,13 @@ import { useNavigateHelper } from '@affine/core/components/hooks/use-navigate-he
import { PageDetailEditor } from '@affine/core/components/page-detail-editor'; import { PageDetailEditor } from '@affine/core/components/page-detail-editor';
import { DetailPageWrapper } from '@affine/core/desktop/pages/workspace/detail-page/detail-page-wrapper'; import { DetailPageWrapper } from '@affine/core/desktop/pages/workspace/detail-page/detail-page-wrapper';
import { PageHeader } from '@affine/core/mobile/components'; import { PageHeader } from '@affine/core/mobile/components';
import { useGlobalEvent } from '@affine/core/mobile/hooks/use-global-events';
import { DocDisplayMetaService } from '@affine/core/modules/doc-display-meta';
import { EditorService } from '@affine/core/modules/editor'; import { EditorService } from '@affine/core/modules/editor';
import { JournalService } from '@affine/core/modules/journal'; import { JournalService } from '@affine/core/modules/journal';
import { WorkbenchService } from '@affine/core/modules/workbench'; import { WorkbenchService } from '@affine/core/modules/workbench';
import { ViewService } from '@affine/core/modules/workbench/services/view'; import { ViewService } from '@affine/core/modules/workbench/services/view';
import { i18nTime } from '@affine/i18n'; import { i18nTime, useI18n } from '@affine/i18n';
import { import {
BookmarkBlockService, BookmarkBlockService,
customImageProxyMiddleware, customImageProxyMiddleware,
@@ -36,11 +38,10 @@ import {
useServices, useServices,
WorkspaceService, WorkspaceService,
} from '@toeverything/infra'; } from '@toeverything/infra';
import { bodyEmphasized } from '@toeverything/theme/typography';
import { cssVarV2 } from '@toeverything/theme/v2'; import { cssVarV2 } from '@toeverything/theme/v2';
import clsx from 'clsx'; import clsx from 'clsx';
import dayjs from 'dayjs'; import dayjs from 'dayjs';
import { useCallback, useEffect, useRef } from 'react'; import { useCallback, useEffect, useRef, useState } from 'react';
import { useParams } from 'react-router-dom'; import { useParams } from 'react-router-dom';
import { AppTabs } from '../../../components'; import { AppTabs } from '../../../components';
@@ -219,6 +220,8 @@ const skeletonWithBack = getSkeleton(true);
const notFound = getNotFound(false); const notFound = getNotFound(false);
const notFoundWithBack = getNotFound(true); const notFoundWithBack = getNotFound(true);
const checkShowTitle = () => window.scrollY >= 158;
const MobileDetailPage = ({ const MobileDetailPage = ({
pageId, pageId,
date, date,
@@ -226,8 +229,15 @@ const MobileDetailPage = ({
pageId: string; pageId: string;
date?: string; date?: string;
}) => { }) => {
const t = useI18n();
const docDisplayMetaService = useService(DocDisplayMetaService);
const journalService = useService(JournalService); const journalService = useService(JournalService);
const [showTitle, setShowTitle] = useState(checkShowTitle);
const { openJournal } = useJournalRouteHelper(); const { openJournal } = useJournalRouteHelper();
const titleInfo = useLiveData(docDisplayMetaService.title$(pageId));
const title =
typeof titleInfo === 'string' ? titleInfo : t[titleInfo.i18nKey]();
const allJournalDates = useLiveData(journalService.allJournalDates$); const allJournalDates = useLiveData(journalService.allJournalDates$);
@@ -237,6 +247,12 @@ const MobileDetailPage = ({
}, },
[openJournal] [openJournal]
); );
useGlobalEvent(
'scroll',
useCallback(() => setShowTitle(checkShowTitle()), [])
);
return ( return (
<div className={styles.root}> <div className={styles.root}>
<DetailPageWrapper <DetailPageWrapper
@@ -247,6 +263,7 @@ const MobileDetailPage = ({
<PageHeader <PageHeader
back={!date} back={!date}
className={styles.header} className={styles.header}
contentClassName={styles.headerContent}
suffix={ suffix={
<> <>
<PageHeaderShareButton /> <PageHeaderShareButton />
@@ -265,11 +282,11 @@ const MobileDetailPage = ({
} }
bottomSpacer={94} bottomSpacer={94}
> >
{date ? ( <span data-show={!!date || showTitle} className={styles.headerTitle}>
<span className={bodyEmphasized}> {date
{i18nTime(dayjs(date), { absolute: { accuracy: 'month' } })} ? i18nTime(dayjs(date), { absolute: { accuracy: 'month' } })
</span> : title}
) : null} </span>
</PageHeader> </PageHeader>
<DetailPageImpl /> <DetailPageImpl />
<AppTabs background={cssVarV2('layer/background/primary')} /> <AppTabs background={cssVarV2('layer/background/primary')} />