mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-07-22 04:26:23 +08:00
219 lines
7.1 KiB
TypeScript
219 lines
7.1 KiB
TypeScript
import { useEffect, useRef, useState, type UIEvent } from 'react';
|
|
import { useParams } from 'react-router';
|
|
|
|
import { AffineEditor } from '@toeverything/components/affine-editor';
|
|
import { CollapsibleTitle } from '@toeverything/components/common';
|
|
import {
|
|
Activities,
|
|
CalendarHeatmap,
|
|
PageTree,
|
|
} from '@toeverything/components/layout';
|
|
import {
|
|
MuiBox as Box,
|
|
MuiCircularProgress as CircularProgress,
|
|
styled,
|
|
} from '@toeverything/components/ui';
|
|
import {
|
|
usePageClientWidth,
|
|
useShowSpaceSidebar,
|
|
} from '@toeverything/datasource/state';
|
|
|
|
import { type BlockEditor } from '@toeverything/components/editor-core';
|
|
import { useFlag } from '@toeverything/datasource/feature-flags';
|
|
import { CollapsiblePageTree } from './collapsible-page-tree';
|
|
import { Tabs } from './components/tabs';
|
|
import { TabMap, TAB_TITLE } from './components/tabs/Tabs';
|
|
import { TOC } from './components/toc';
|
|
import { WorkspaceName } from './workspace-name';
|
|
|
|
type PageProps = {
|
|
workspace: string;
|
|
};
|
|
|
|
export function Page(props: PageProps) {
|
|
const [activeTab, setActiveTab] = useState(
|
|
TabMap.get(TAB_TITLE.PAGES).value
|
|
);
|
|
const { pageId } = useParams();
|
|
const { showSpaceSidebar, fixedDisplay, setSpaceSidebarVisible } =
|
|
useShowSpaceSidebar();
|
|
const dailyNotesFlag = useFlag('BooleanDailyNotes', false);
|
|
const onTabChange = v => setActiveTab(v);
|
|
|
|
return (
|
|
<LigoApp>
|
|
<LigoLeftContainer style={{ width: fixedDisplay ? '300px' : 0 }}>
|
|
<WorkspaceSidebar
|
|
style={{
|
|
opacity: !showSpaceSidebar && !fixedDisplay ? 0 : 1,
|
|
transform:
|
|
!showSpaceSidebar && !fixedDisplay
|
|
? 'translateX(-270px)'
|
|
: 'translateX(0px)',
|
|
transition: '0.8s',
|
|
}}
|
|
onMouseEnter={() => setSpaceSidebarVisible(true)}
|
|
onMouseLeave={() => setSpaceSidebarVisible(false)}
|
|
>
|
|
<WorkspaceName />
|
|
|
|
<Tabs activeTab={activeTab} onTabChange={onTabChange} />
|
|
|
|
<WorkspaceSidebarContent>
|
|
{activeTab === TabMap.get(TAB_TITLE.PAGES).value && (
|
|
<div>
|
|
{dailyNotesFlag && (
|
|
<div>
|
|
<CollapsibleTitle title="Daily Notes">
|
|
<CalendarHeatmap />
|
|
</CollapsibleTitle>
|
|
</div>
|
|
)}
|
|
<div>
|
|
<CollapsibleTitle
|
|
title="ACTIVITIES"
|
|
initialOpen={false}
|
|
>
|
|
<Activities />
|
|
</CollapsibleTitle>
|
|
</div>
|
|
<div>
|
|
<CollapsiblePageTree title="PAGES">
|
|
{pageId ? <PageTree /> : null}
|
|
</CollapsiblePageTree>
|
|
</div>
|
|
</div>
|
|
)}
|
|
|
|
{activeTab === TabMap.get(TAB_TITLE.TOC).value && (
|
|
<TOC />
|
|
)}
|
|
{activeTab === TabMap.get(TAB_TITLE.GALLERY).value && (
|
|
<StyledTextForGallery>
|
|
Gallery function coming soon...
|
|
</StyledTextForGallery>
|
|
)}
|
|
</WorkspaceSidebarContent>
|
|
</WorkspaceSidebar>
|
|
</LigoLeftContainer>
|
|
<EditorContainer workspace={props.workspace} pageId={pageId} />
|
|
</LigoApp>
|
|
);
|
|
}
|
|
|
|
const EditorContainer = ({
|
|
pageId,
|
|
workspace,
|
|
}: {
|
|
pageId: string;
|
|
workspace: string;
|
|
}) => {
|
|
const [lockScroll, setLockScroll] = useState(false);
|
|
const [scrollContainer, setScrollContainer] = useState<HTMLElement>();
|
|
const editorRef = useRef<BlockEditor>();
|
|
|
|
const onScroll = (event: UIEvent) => {
|
|
editorRef.current.getHooks().onRootNodeScroll(event);
|
|
editorRef.current.scrollManager.emitScrollEvent(event);
|
|
};
|
|
|
|
const { setPageClientWidth } = usePageClientWidth();
|
|
useEffect(() => {
|
|
if (scrollContainer) {
|
|
const obv = new ResizeObserver(e => {
|
|
setPageClientWidth(e[0].contentRect.width);
|
|
});
|
|
|
|
obv.observe(scrollContainer);
|
|
return () => obv.disconnect();
|
|
}
|
|
}, [setPageClientWidth, scrollContainer]);
|
|
|
|
return (
|
|
<StyledEditorContainer
|
|
lockScroll={lockScroll}
|
|
ref={ref => setScrollContainer(ref)}
|
|
onScroll={onScroll}
|
|
>
|
|
{pageId ? (
|
|
<AffineEditor
|
|
workspace={workspace}
|
|
rootBlockId={pageId}
|
|
ref={editorRef}
|
|
scrollContainer={scrollContainer}
|
|
scrollController={{
|
|
lockScroll: () => setLockScroll(true),
|
|
unLockScroll: () => setLockScroll(false),
|
|
}}
|
|
/>
|
|
) : (
|
|
<Box
|
|
sx={{
|
|
display: 'flex',
|
|
justifyContent: 'center',
|
|
alignItems: 'center',
|
|
width: '100%',
|
|
}}
|
|
>
|
|
<CircularProgress />
|
|
</Box>
|
|
)}
|
|
</StyledEditorContainer>
|
|
);
|
|
};
|
|
|
|
const StyledEditorContainer = styled('div')<{ lockScroll: boolean }>(
|
|
({ lockScroll }) => {
|
|
return {
|
|
width: '100%',
|
|
overflowY: lockScroll ? 'hidden' : 'auto',
|
|
flex: 'auto',
|
|
};
|
|
}
|
|
);
|
|
|
|
const LigoApp = styled('div')({
|
|
width: '100vw',
|
|
display: 'flex',
|
|
flex: '1 1 0%',
|
|
backgroundColor: 'white',
|
|
});
|
|
|
|
const LigoLeftContainer = styled('div')({
|
|
flex: '0 0 auto',
|
|
position: 'relative',
|
|
});
|
|
|
|
const WorkspaceSidebar = styled('div')(({ theme }) => ({
|
|
position: 'absolute',
|
|
bottom: '48px',
|
|
top: '12px',
|
|
zIndex: 1,
|
|
display: 'flex',
|
|
flexDirection: 'column',
|
|
width: 300,
|
|
minWidth: 300,
|
|
borderRadius: '0px 10px 10px 0px',
|
|
boxShadow: theme.affine.shadows.shadow2,
|
|
backgroundColor: '#FFFFFF',
|
|
transitionProperty: 'left',
|
|
transitionDuration: '0.35s',
|
|
transitionTimingFunction: 'ease',
|
|
padding: '16px 12px',
|
|
}));
|
|
|
|
const WorkspaceSidebarContent = styled('div')({
|
|
flex: 'auto',
|
|
overflow: 'hidden auto',
|
|
marginTop: '18px',
|
|
});
|
|
|
|
const StyledTextForGallery = styled('div')(({ theme }) => {
|
|
return {
|
|
display: 'flex',
|
|
justifyContent: 'center',
|
|
color: theme.affine.palette.menu,
|
|
marginTop: theme.affine.spacing.lgSpacing,
|
|
};
|
|
});
|