refactor: workspace header (#1880)

This commit is contained in:
Himself65
2023-04-11 21:39:39 -05:00
committed by GitHub
parent 2e823c2fee
commit a06113d48c
42 changed files with 1653 additions and 470 deletions

View File

@@ -1,119 +0,0 @@
import { useTranslation } from '@affine/i18n';
import { CloseIcon } from '@blocksuite/icons';
import type { HTMLAttributes, PropsWithChildren } from 'react';
import type React from 'react';
import { forwardRef, useEffect, useMemo, useState } from 'react';
import {
useSidebarFloating,
useSidebarStatus,
} from '../../../hooks/use-sidebar-status';
import { SidebarSwitch } from '../../affine/sidebar-switch';
import { EditorOptionMenu } from './header-right-items/EditorOptionMenu';
import SyncUser from './header-right-items/SyncUser';
import ThemeModeSwitch from './header-right-items/theme-mode-switch';
import TrashButtonGroup from './header-right-items/TrashButtonGroup';
import {
StyledBrowserWarning,
StyledCloseButton,
StyledHeader,
StyledHeaderContainer,
StyledHeaderRightSide,
} from './styles';
import { OSWarningMessage, shouldShowWarning } from './utils';
const BrowserWarning = ({
show,
onClose,
}: {
show: boolean;
onClose: () => void;
}) => {
return (
<StyledBrowserWarning show={show}>
<OSWarningMessage />
<StyledCloseButton onClick={onClose}>
<CloseIcon />
</StyledCloseButton>
</StyledBrowserWarning>
);
};
type HeaderRightItemNames =
| 'editorOptionMenu'
| 'trashButtonGroup'
| 'themeModeSwitch'
| 'syncUser';
const HeaderRightItems: Record<HeaderRightItemNames, React.FC> = {
editorOptionMenu: EditorOptionMenu,
trashButtonGroup: TrashButtonGroup,
themeModeSwitch: ThemeModeSwitch,
syncUser: SyncUser,
};
export type HeaderProps = PropsWithChildren<{
rightItems?: HeaderRightItemNames[];
}>;
export const Header = forwardRef<
HTMLDivElement,
HeaderProps & HTMLAttributes<HTMLDivElement>
>(
(
{ rightItems = ['syncUser', 'themeModeSwitch'], children, ...props },
ref
) => {
const [showWarning, setShowWarning] = useState(false);
useEffect(() => {
setShowWarning(shouldShowWarning());
}, []);
const [open] = useSidebarStatus();
const sidebarFloating = useSidebarFloating();
const { t } = useTranslation();
return (
<StyledHeaderContainer
sidebarFloating={sidebarFloating && open}
ref={ref}
hasWarning={showWarning}
{...props}
>
<BrowserWarning
show={showWarning}
onClose={() => {
setShowWarning(false);
}}
/>
<StyledHeader
hasWarning={showWarning}
data-testid="editor-header-items"
data-tauri-drag-region
>
<SidebarSwitch
visible={!open}
tooltipContent={t('Expand sidebar')}
testid="sliderBar-arrowButton-expand"
/>
{children}
<StyledHeaderRightSide>
{useMemo(
() =>
rightItems.map(itemName => {
const Item = HeaderRightItems[itemName];
return <Item key={itemName} />;
}),
[rightItems]
)}
{/*<ShareMenu />*/}
</StyledHeaderRightSide>
</StyledHeader>
</StyledHeaderContainer>
);
}
);
Header.displayName = 'Header';
export default Header;

View File

@@ -1,158 +0,0 @@
import type { PopperProps } from '@affine/component';
import { QuickSearchTips } from '@affine/component';
import { getEnvironment } from '@affine/env';
import { ArrowDownSmallIcon } from '@blocksuite/icons';
import { assertExists } from '@blocksuite/store';
import { useAtomValue, useSetAtom } from 'jotai';
import type { HTMLAttributes } from 'react';
import { forwardRef, useCallback, useRef } from 'react';
import { currentEditorAtom, openQuickSearchModalAtom } from '../../../atoms';
import { useGuideHidden } from '../../../hooks/use-is-first-load';
import { usePageMeta } from '../../../hooks/use-page-meta';
import { useElementResizeEffect } from '../../../hooks/use-workspaces';
import type { BlockSuiteWorkspace } from '../../../shared';
import { PageNotFoundError } from '../../affine/affine-error-eoundary';
import { QuickSearchButton } from '../../pure/quick-search-button';
import { EditorModeSwitch } from './editor-mode-switch';
import Header from './header';
import {
StyledQuickSearchTipButton,
StyledQuickSearchTipContent,
StyledSearchArrowWrapper,
StyledSwitchWrapper,
StyledTitle,
StyledTitleContainer,
StyledTitleWrapper,
} from './styles';
export type BlockSuiteEditorHeaderProps = React.PropsWithChildren<{
blockSuiteWorkspace: BlockSuiteWorkspace;
pageId: string;
isPublic?: boolean;
isPreview?: boolean;
}>;
export const BlockSuiteEditorHeader = forwardRef<
HTMLDivElement,
BlockSuiteEditorHeaderProps & HTMLAttributes<HTMLDivElement>
>(
(
{ blockSuiteWorkspace, pageId, children, isPublic, isPreview, ...props },
ref
) => {
const page = blockSuiteWorkspace.getPage(pageId);
// fixme(himself65): remove this atom and move it to props
const setOpenQuickSearch = useSetAtom(openQuickSearchModalAtom);
if (!page) {
throw new PageNotFoundError(blockSuiteWorkspace, pageId);
}
const pageMeta = usePageMeta(blockSuiteWorkspace).find(
meta => meta.id === pageId
);
assertExists(pageMeta);
const title = pageMeta.title;
const { trash: isTrash } = pageMeta;
const [isTipsHidden, setTipsHidden] = useGuideHidden();
const isMac = () => {
const env = getEnvironment();
return env.isBrowser && env.isMacOs;
};
const popperRef: PopperProps['popperRef'] = useRef(null);
useElementResizeEffect(
useAtomValue(currentEditorAtom),
useCallback(() => {
if (isTipsHidden.quickSearchTips || !popperRef.current) {
return;
}
popperRef.current.update();
}, [isTipsHidden.quickSearchTips])
);
const TipsContent = (
<StyledQuickSearchTipContent>
<div>
Click button
{
<span
style={{
fontSize: '24px',
verticalAlign: 'middle',
}}
>
<ArrowDownSmallIcon />
</span>
}
or use
{isMac() ? ' ⌘ + K' : ' Ctrl + K'} to activate Quick Search. Then you
can search keywords or quickly open recently viewed pages.
</div>
<StyledQuickSearchTipButton
data-testid="quick-search-got-it"
onClick={() =>
setTipsHidden({ ...isTipsHidden, quickSearchTips: true })
}
>
Got it
</StyledQuickSearchTipButton>
</StyledQuickSearchTipContent>
);
return (
<Header
ref={ref}
rightItems={
// fixme(himself65): other right items not supported in public mode
isPublic || isPreview
? ['themeModeSwitch']
: isTrash
? ['trashButtonGroup']
: [
'syncUser',
/* 'shareMenu', */ 'themeModeSwitch',
'editorOptionMenu',
]
}
{...props}
>
{children}
{!isPublic && (
<StyledTitleContainer data-tauri-drag-region>
<StyledTitleWrapper>
<StyledSwitchWrapper>
<EditorModeSwitch
blockSuiteWorkspace={blockSuiteWorkspace}
pageId={pageId}
style={{
marginRight: '12px',
}}
/>
</StyledSwitchWrapper>
<StyledTitle>{title || 'Untitled'}</StyledTitle>
<QuickSearchTips
data-testid="quick-search-tips"
content={TipsContent}
placement="bottom"
popperRef={popperRef}
open={!isTipsHidden.quickSearchTips}
offset={[0, -5]}
>
<StyledSearchArrowWrapper>
<QuickSearchButton
onClick={() => {
setOpenQuickSearch(true);
}}
/>
</StyledSearchArrowWrapper>
</QuickSearchTips>
</StyledTitleWrapper>
</StyledTitleContainer>
)}
</Header>
);
}
);
BlockSuiteEditorHeader.displayName = 'BlockSuiteEditorHeader';

View File

@@ -0,0 +1,669 @@
{
"v": "5.9.0",
"fr": 29.9700012207031,
"ip": 0,
"op": 60.0000024438501,
"w": 500,
"h": 500,
"nm": "edgeless-hover",
"ddd": 0,
"assets": [],
"layers": [
{
"ddd": 0,
"ind": 1,
"ty": 4,
"nm": "Layer 3/paper-edgeless-icons Outlines",
"sr": 1,
"ks": {
"o": { "a": 0, "k": 100, "ix": 11 },
"r": { "a": 0, "k": 0, "ix": 10 },
"p": { "a": 0, "k": [250, 250, 0], "ix": 2, "l": 2 },
"a": { "a": 0, "k": [183.5, 183.5, 0], "ix": 1, "l": 2 },
"s": { "a": 0, "k": [100, 100, 100], "ix": 6, "l": 2 }
},
"ao": 0,
"shapes": [
{
"ty": "gr",
"it": [
{
"ind": 0,
"ty": "sh",
"ix": 1,
"ks": {
"a": 0,
"k": {
"i": [
[0, 0],
[0, -5.604],
[0, 0],
[-5.614, 0],
[0, 0],
[0, 5.605],
[0, 0],
[5.615, 0]
],
"o": [
[-5.614, 0],
[0, 0],
[0, 5.605],
[0, 0],
[5.615, 0],
[0, 0],
[0, -5.604],
[0, 0]
],
"v": [
[-30.525, -40.7],
[-40.699, -30.525],
[-40.699, 30.524],
[-30.525, 40.699],
[30.525, 40.699],
[40.699, 30.524],
[40.699, -30.525],
[30.525, -40.7]
],
"c": true
},
"ix": 2
},
"nm": "Path 1",
"mn": "ADBE Vector Shape - Group",
"hd": false
},
{
"ind": 1,
"ty": "sh",
"ix": 2,
"ks": {
"a": 0,
"k": {
"i": [
[22.447, 0],
[0, 0],
[0, 22.437],
[0, 0],
[-22.446, 0],
[0, 0],
[0, -22.437],
[0, 0]
],
"o": [
[0, 0],
[-22.446, 0],
[0, 0],
[0, -22.437],
[0, 0],
[22.447, 0],
[0, 0],
[0, 22.437]
],
"v": [
[30.525, 71.224],
[-30.525, 71.224],
[-71.225, 30.524],
[-71.225, -30.525],
[-30.525, -71.225],
[30.525, -71.225],
[71.224, -30.525],
[71.224, 30.524]
],
"c": true
},
"ix": 2
},
"nm": "Path 2",
"mn": "ADBE Vector Shape - Group",
"hd": false
},
{
"ty": "mm",
"mm": 1,
"nm": "Merge Paths 1",
"mn": "ADBE Vector Filter - Merge",
"hd": false
},
{
"ty": "fl",
"c": {
"a": 0,
"k": [0.466666696586, 0.458823559331, 0.490196108351, 1],
"ix": 4
},
"o": { "a": 0, "k": 100, "ix": 5 },
"r": 1,
"bm": 0,
"nm": "Fill 1",
"mn": "ADBE Vector Graphic - Fill",
"hd": false
},
{
"ty": "tr",
"p": {
"a": 1,
"k": [
{
"i": { "x": 0.833, "y": 0.833 },
"o": { "x": 0.167, "y": 0.167 },
"t": 0,
"s": [295.322, 295.323],
"to": [0, 0],
"ti": [0, 0]
},
{
"i": { "x": 0.833, "y": 0.833 },
"o": { "x": 0.167, "y": 0.167 },
"t": 30,
"s": [315.322, 315.323],
"to": [0, 0],
"ti": [0, 0]
},
{ "t": 58.0000023623884, "s": [295.322, 295.323] }
],
"ix": 2
},
"a": { "a": 0, "k": [0, 0], "ix": 1 },
"s": { "a": 0, "k": [100, 100], "ix": 3 },
"r": { "a": 0, "k": 0, "ix": 6 },
"o": { "a": 0, "k": 100, "ix": 7 },
"sk": { "a": 0, "k": 0, "ix": 4 },
"sa": { "a": 0, "k": 0, "ix": 5 },
"nm": "Transform"
}
],
"nm": "Group 1",
"np": 4,
"cix": 2,
"bm": 0,
"ix": 1,
"mn": "ADBE Vector Group",
"hd": false
},
{
"ty": "gr",
"it": [
{
"ind": 0,
"ty": "sh",
"ix": 1,
"ks": {
"a": 0,
"k": {
"i": [
[8.426, 0],
[0, 8.426],
[0, 0],
[-8.426, 0],
[0, -8.426],
[0, 0]
],
"o": [
[-8.426, 0],
[0, 0],
[0, -8.426],
[8.426, 0],
[0, 0],
[0, 8.426]
],
"v": [
[0, 30.525],
[-15.262, 15.263],
[-15.262, -15.262],
[0, -30.525],
[15.262, -15.262],
[15.262, 15.263]
],
"c": true
},
"ix": 2
},
"nm": "Path 1",
"mn": "ADBE Vector Shape - Group",
"hd": false
},
{
"ty": "fl",
"c": {
"a": 0,
"k": [0.466666696586, 0.458823559331, 0.490196108351, 1],
"ix": 4
},
"o": { "a": 0, "k": 100, "ix": 5 },
"r": 1,
"bm": 0,
"nm": "Fill 1",
"mn": "ADBE Vector Graphic - Fill",
"hd": false
},
{
"ty": "tr",
"p": {
"a": 1,
"k": [
{
"i": { "x": 0.833, "y": 0.833 },
"o": { "x": 0.167, "y": 0.167 },
"t": 0,
"s": [76.561, 183.399],
"to": [0, 0],
"ti": [0, 0]
},
{
"i": { "x": 0.833, "y": 0.833 },
"o": { "x": 0.167, "y": 0.167 },
"t": 30,
"s": [56.561, 183.399],
"to": [0, 0],
"ti": [0, 0]
},
{ "t": 58.0000023623884, "s": [76.561, 183.399] }
],
"ix": 2
},
"a": { "a": 0, "k": [0, 0], "ix": 1 },
"s": { "a": 0, "k": [100, 100], "ix": 3 },
"r": { "a": 0, "k": 0, "ix": 6 },
"o": { "a": 0, "k": 100, "ix": 7 },
"sk": { "a": 0, "k": 0, "ix": 4 },
"sa": { "a": 0, "k": 0, "ix": 5 },
"nm": "Transform"
}
],
"nm": "Group 2",
"np": 2,
"cix": 2,
"bm": 0,
"ix": 2,
"mn": "ADBE Vector Group",
"hd": false
},
{
"ty": "gr",
"it": [
{
"ind": 0,
"ty": "sh",
"ix": 1,
"ks": {
"a": 0,
"k": {
"i": [
[8.426, 0],
[0, 8.426],
[5.395, 13.057],
[9.956, 9.956],
[13.037, 5.406],
[14.1, 0],
[0, 8.426],
[-8.426, 0],
[-16.753, -6.955],
[-12.808, -12.818],
[-6.955, -16.773],
[0, -18.105]
],
"o": [
[-8.426, 0],
[0, -14.09],
[-5.416, -13.016],
[-9.957, -9.956],
[-13.026, -5.385],
[-8.426, 0],
[0, -8.426],
[18.134, 0],
[16.763, 6.936],
[12.788, 12.778],
[6.936, 16.773],
[0, 8.426]
],
"v": [
[61.049, 76.312],
[45.788, 61.05],
[37.66, 20.151],
[14.497, -14.487],
[-20.161, -37.659],
[-61.049, -45.787],
[-76.311, -61.049],
[-61.049, -76.312],
[-8.475, -65.839],
[36.09, -36.069],
[65.858, 8.486],
[76.311, 61.05]
],
"c": true
},
"ix": 2
},
"nm": "Path 1",
"mn": "ADBE Vector Shape - Group",
"hd": false
},
{
"ty": "fl",
"c": {
"a": 0,
"k": [0.466666696586, 0.458823559331, 0.490196108351, 1],
"ix": 4
},
"o": { "a": 0, "k": 100, "ix": 5 },
"r": 1,
"bm": 0,
"nm": "Fill 1",
"mn": "ADBE Vector Graphic - Fill",
"hd": false
},
{
"ty": "tr",
"p": {
"a": 1,
"k": [
{
"i": { "x": 0.833, "y": 0.833 },
"o": { "x": 0.167, "y": 0.167 },
"t": 0,
"s": [254.447, 112.349],
"to": [0, 0],
"ti": [0, 0]
},
{
"i": { "x": 0.833, "y": 0.833 },
"o": { "x": 0.167, "y": 0.167 },
"t": 30,
"s": [274.447, 92.349],
"to": [0, 0],
"ti": [0, 0]
},
{ "t": 58.0000023623884, "s": [254.447, 112.349] }
],
"ix": 2
},
"a": { "a": 0, "k": [0, 0], "ix": 1 },
"s": { "a": 0, "k": [100, 100], "ix": 3 },
"r": { "a": 0, "k": 0, "ix": 6 },
"o": { "a": 0, "k": 100, "ix": 7 },
"sk": { "a": 0, "k": 0, "ix": 4 },
"sa": { "a": 0, "k": 0, "ix": 5 },
"nm": "Transform"
}
],
"nm": "Group 3",
"np": 2,
"cix": 2,
"bm": 0,
"ix": 3,
"mn": "ADBE Vector Group",
"hd": false
},
{
"ty": "gr",
"it": [
{
"ind": 0,
"ty": "sh",
"ix": 1,
"ks": {
"a": 0,
"k": {
"i": [
[22.446, 0],
[0, -22.437],
[-22.446, 0],
[0, 22.436]
],
"o": [
[-22.446, 0],
[0, 22.436],
[22.446, 0],
[0, -22.437]
],
"v": [
[0, -40.7],
[-40.7, 0],
[0, 40.699],
[40.7, 0]
],
"c": true
},
"ix": 2
},
"nm": "Path 1",
"mn": "ADBE Vector Shape - Group",
"hd": false
},
{
"ind": 1,
"ty": "sh",
"ix": 2,
"ks": {
"a": 0,
"k": {
"i": [
[39.269, 0],
[0, 39.268],
[-39.269, 0],
[0, -39.269]
],
"o": [
[-39.269, 0],
[0, -39.269],
[39.269, 0],
[0, 39.268]
],
"v": [
[0, 71.224],
[-71.224, 0],
[0, -71.225],
[71.224, 0]
],
"c": true
},
"ix": 2
},
"nm": "Path 2",
"mn": "ADBE Vector Shape - Group",
"hd": false
},
{
"ty": "mm",
"mm": 1,
"nm": "Merge Paths 1",
"mn": "ADBE Vector Filter - Merge",
"hd": false
},
{
"ty": "fl",
"c": {
"a": 0,
"k": [0.466666696586, 0.458823559331, 0.490196108351, 1],
"ix": 4
},
"o": { "a": 0, "k": 100, "ix": 5 },
"r": 1,
"bm": 0,
"nm": "Fill 1",
"mn": "ADBE Vector Graphic - Fill",
"hd": false
},
{
"ty": "tr",
"p": {
"a": 1,
"k": [
{
"i": { "x": 0.833, "y": 0.833 },
"o": { "x": 0.167, "y": 0.167 },
"t": 0,
"s": [71.474, 295.323],
"to": [0, 0],
"ti": [0, 0]
},
{
"i": { "x": 0.833, "y": 0.833 },
"o": { "x": 0.167, "y": 0.167 },
"t": 30,
"s": [51.474, 315.323],
"to": [0, 0],
"ti": [0, 0]
},
{ "t": 58.0000023623884, "s": [71.474, 295.323] }
],
"ix": 2
},
"a": { "a": 0, "k": [0, 0], "ix": 1 },
"s": { "a": 0, "k": [100, 100], "ix": 3 },
"r": { "a": 0, "k": 0, "ix": 6 },
"o": { "a": 0, "k": 100, "ix": 7 },
"sk": { "a": 0, "k": 0, "ix": 4 },
"sa": { "a": 0, "k": 0, "ix": 5 },
"nm": "Transform"
}
],
"nm": "Group 4",
"np": 4,
"cix": 2,
"bm": 0,
"ix": 4,
"mn": "ADBE Vector Group",
"hd": false
},
{
"ty": "gr",
"it": [
{
"ind": 0,
"ty": "sh",
"ix": 1,
"ks": {
"a": 0,
"k": {
"i": [
[22.446, 0],
[0, -22.437],
[-22.446, 0],
[0, 22.436]
],
"o": [
[-22.446, 0],
[0, 22.436],
[22.446, 0],
[0, -22.437]
],
"v": [
[0, -40.7],
[-40.7, 0.001],
[0, 40.7],
[40.7, 0.001]
],
"c": true
},
"ix": 2
},
"nm": "Path 1",
"mn": "ADBE Vector Shape - Group",
"hd": false
},
{
"ind": 1,
"ty": "sh",
"ix": 2,
"ks": {
"a": 0,
"k": {
"i": [
[39.269, 0],
[0, 39.268],
[-39.269, 0],
[0, -39.269]
],
"o": [
[-39.269, 0],
[0, -39.269],
[39.269, 0],
[0, 39.268]
],
"v": [
[0, 71.225],
[-71.224, 0.001],
[0, -71.225],
[71.224, 0.001]
],
"c": true
},
"ix": 2
},
"nm": "Path 2",
"mn": "ADBE Vector Shape - Group",
"hd": false
},
{
"ty": "mm",
"mm": 1,
"nm": "Merge Paths 1",
"mn": "ADBE Vector Filter - Merge",
"hd": false
},
{
"ty": "fl",
"c": {
"a": 0,
"k": [0.466666696586, 0.458823559331, 0.490196108351, 1],
"ix": 4
},
"o": { "a": 0, "k": 100, "ix": 5 },
"r": 1,
"bm": 0,
"nm": "Fill 1",
"mn": "ADBE Vector Graphic - Fill",
"hd": false
},
{
"ty": "tr",
"p": {
"a": 1,
"k": [
{
"i": { "x": 0.833, "y": 0.833 },
"o": { "x": 0.167, "y": 0.167 },
"t": 0,
"s": [71.474, 71.475],
"to": [0, 0],
"ti": [0, 0]
},
{
"i": { "x": 0.833, "y": 0.833 },
"o": { "x": 0.167, "y": 0.167 },
"t": 30,
"s": [51.474, 51.475],
"to": [0, 0],
"ti": [0, 0]
},
{ "t": 58.0000023623884, "s": [71.474, 71.475] }
],
"ix": 2
},
"a": { "a": 0, "k": [0, 0], "ix": 1 },
"s": { "a": 0, "k": [100, 100], "ix": 3 },
"r": { "a": 0, "k": 0, "ix": 6 },
"o": { "a": 0, "k": 100, "ix": 7 },
"sk": { "a": 0, "k": 0, "ix": 4 },
"sa": { "a": 0, "k": 0, "ix": 5 },
"nm": "Transform"
}
],
"nm": "Group 5",
"np": 4,
"cix": 2,
"bm": 0,
"ix": 5,
"mn": "ADBE Vector Group",
"hd": false
}
],
"ip": 0,
"op": 60.0000024438501,
"st": 0,
"bm": 0
}
],
"markers": []
}

View File

@@ -0,0 +1,521 @@
{
"v": "5.9.0",
"fr": 29.9700012207031,
"ip": 0,
"op": 60.0000024438501,
"w": 500,
"h": 500,
"nm": "page-hover",
"ddd": 0,
"assets": [],
"layers": [
{
"ddd": 0,
"ind": 1,
"ty": 4,
"nm": "Layer 1/paper-edgeless-icons Outlines",
"sr": 1,
"ks": {
"o": { "a": 0, "k": 100, "ix": 11 },
"r": { "a": 0, "k": 0, "ix": 10 },
"p": { "a": 0, "k": [250, 250, 0], "ix": 2, "l": 2 },
"a": { "a": 0, "k": [183, 183, 0], "ix": 1, "l": 2 },
"s": { "a": 0, "k": [100, 100, 100], "ix": 6, "l": 2 }
},
"ao": 0,
"shapes": [
{
"ty": "gr",
"it": [
{
"ind": 0,
"ty": "sh",
"ix": 1,
"ks": {
"a": 0,
"k": {
"i": [
[22.557, 0],
[0, 0],
[0, 8.639],
[-8.64, 0],
[0, 0],
[-2.201, 1.651],
[0, 9.536],
[0, 0],
[-8.64, 0],
[0, -8.64],
[0, 0],
[16.892, -16.892]
],
"o": [
[0, 0],
[-8.64, 0],
[0, -8.64],
[0, 0],
[17.442, 0],
[10.412, -10.453],
[0, 0],
[0, -8.64],
[8.639, 0],
[0, 0],
[0, 18.155],
[-9.027, 8.987]
],
"v": [
[46.947, 182.579],
[-109.545, 182.579],
[-125.194, 166.93],
[-109.545, 151.281],
[46.947, 151.281],
[78.001, 145.086],
[93.895, 114.766],
[93.895, -166.93],
[109.545, -182.579],
[125.194, -166.93],
[125.194, 114.766],
[99.743, 167.561]
],
"c": true
},
"ix": 2
},
"nm": "Path 1",
"mn": "ADBE Vector Shape - Group",
"hd": false
},
{
"ty": "fl",
"c": {
"a": 0,
"k": [0.466666696586, 0.458823559331, 0.490196108351, 1],
"ix": 4
},
"o": { "a": 0, "k": 100, "ix": 5 },
"r": 1,
"bm": 0,
"nm": "Fill 1",
"mn": "ADBE Vector Graphic - Fill",
"hd": false
},
{
"ty": "tr",
"p": {
"a": 1,
"k": [
{
"i": { "x": 0.833, "y": 0.833 },
"o": { "x": 0.167, "y": 0.167 },
"t": 0,
"s": [240.204, 182.829],
"to": [0, 0],
"ti": [0, 0]
},
{
"i": { "x": 0.833, "y": 0.833 },
"o": { "x": 0.167, "y": 0.167 },
"t": 30,
"s": [260.204, 202.829],
"to": [0, 0],
"ti": [0, 0]
},
{ "t": 58.0000023623884, "s": [240.204, 182.829] }
],
"ix": 2
},
"a": { "a": 0, "k": [0, 0], "ix": 1 },
"s": { "a": 0, "k": [100, 100], "ix": 3 },
"r": { "a": 0, "k": 0, "ix": 6 },
"o": { "a": 0, "k": 100, "ix": 7 },
"sk": { "a": 0, "k": 0, "ix": 4 },
"sa": { "a": 0, "k": 0, "ix": 5 },
"nm": "Transform"
}
],
"nm": "Group 1",
"np": 2,
"cix": 2,
"bm": 0,
"ix": 1,
"mn": "ADBE Vector Group",
"hd": false
},
{
"ty": "gr",
"it": [
{
"ind": 0,
"ty": "sh",
"ix": 1,
"ks": {
"a": 0,
"k": {
"i": [
[8.64, 0],
[0, 8.639],
[0, 0],
[-16.892, 16.882],
[-22.598, 0],
[0, 0],
[0, -8.64],
[8.639, 0],
[0, 0],
[2.171, -1.65],
[0, -9.546],
[0, 0]
],
"o": [
[-8.64, 0],
[0, 0],
[0, -18.145],
[8.976, -8.986],
[0, 0],
[8.639, 0],
[0, 8.64],
[0, 0],
[-17.453, 0],
[-10.443, 10.484],
[0, 0],
[0, 8.639]
],
"v": [
[-109.545, 182.579],
[-125.194, 166.93],
[-125.194, -114.766],
[-99.743, -167.562],
[-46.948, -182.579],
[109.545, -182.579],
[125.194, -166.93],
[109.545, -151.281],
[-46.948, -151.281],
[-77.972, -145.107],
[-93.896, -114.766],
[-93.896, 166.93]
],
"c": true
},
"ix": 2
},
"nm": "Path 1",
"mn": "ADBE Vector Shape - Group",
"hd": false
},
{
"ty": "fl",
"c": {
"a": 0,
"k": [0.466666696586, 0.458823559331, 0.490196108351, 1],
"ix": 4
},
"o": { "a": 0, "k": 100, "ix": 5 },
"r": 1,
"bm": 0,
"nm": "Fill 1",
"mn": "ADBE Vector Graphic - Fill",
"hd": false
},
{
"ty": "tr",
"p": {
"a": 1,
"k": [
{
"i": { "x": 0.833, "y": 0.833 },
"o": { "x": 0.167, "y": 0.167 },
"t": 0,
"s": [125.443, 182.829],
"to": [0, 0],
"ti": [0, 0]
},
{
"i": { "x": 0.833, "y": 0.833 },
"o": { "x": 0.167, "y": 0.167 },
"t": 30,
"s": [105.443, 162.829],
"to": [0, 0],
"ti": [0, 0]
},
{ "t": 58.0000023623884, "s": [125.443, 182.829] }
],
"ix": 2
},
"a": { "a": 0, "k": [0, 0], "ix": 1 },
"s": { "a": 0, "k": [100, 100], "ix": 3 },
"r": { "a": 0, "k": 0, "ix": 6 },
"o": { "a": 0, "k": 100, "ix": 7 },
"sk": { "a": 0, "k": 0, "ix": 4 },
"sa": { "a": 0, "k": 0, "ix": 5 },
"nm": "Transform"
}
],
"nm": "Group 2",
"np": 2,
"cix": 2,
"bm": 0,
"ix": 2,
"mn": "ADBE Vector Group",
"hd": false
},
{
"ty": "gr",
"it": [
{
"ind": 0,
"ty": "sh",
"ix": 1,
"ks": {
"a": 0,
"k": {
"i": [
[8.64, 0],
[0, 0],
[0, 8.639],
[-8.64, 0],
[0, 0],
[0, -8.64]
],
"o": [
[0, 0],
[-8.64, 0],
[0, -8.64],
[0, 0],
[8.64, 0],
[0, 8.639]
],
"v": [
[67.813, 15.649],
[-67.813, 15.649],
[-83.463, 0],
[-67.813, -15.649],
[67.813, -15.649],
[83.462, 0]
],
"c": true
},
"ix": 2
},
"nm": "Path 1",
"mn": "ADBE Vector Shape - Group",
"hd": false
},
{
"ty": "fl",
"c": {
"a": 0,
"k": [0.466666696586, 0.458823559331, 0.490196108351, 1],
"ix": 4
},
"o": { "a": 0, "k": 100, "ix": 5 },
"r": 1,
"bm": 0,
"nm": "Fill 1",
"mn": "ADBE Vector Graphic - Fill",
"hd": false
},
{
"ty": "tr",
"p": {
"a": 1,
"k": [
{
"i": { "x": 0.833, "y": 0.833 },
"o": { "x": 0.167, "y": 0.167 },
"t": 0,
"s": [182.824, 271.513],
"to": [0, 0],
"ti": [0, 0]
},
{
"i": { "x": 0.833, "y": 0.833 },
"o": { "x": 0.167, "y": 0.167 },
"t": 30,
"s": [212.824, 281.513],
"to": [0, 0],
"ti": [0, 0]
},
{ "t": 58.0000023623884, "s": [182.824, 271.513] }
],
"ix": 2
},
"a": { "a": 0, "k": [0, 0], "ix": 1 },
"s": { "a": 0, "k": [100, 100], "ix": 3 },
"r": { "a": 0, "k": 0, "ix": 6 },
"o": { "a": 0, "k": 100, "ix": 7 },
"sk": { "a": 0, "k": 0, "ix": 4 },
"sa": { "a": 0, "k": 0, "ix": 5 },
"nm": "Transform"
}
],
"nm": "Group 3",
"np": 2,
"cix": 2,
"bm": 0,
"ix": 3,
"mn": "ADBE Vector Group",
"hd": false
},
{
"ty": "gr",
"it": [
{
"ind": 0,
"ty": "sh",
"ix": 1,
"ks": {
"a": 0,
"k": {
"i": [
[0, 0],
[0, -5.757],
[0, 0],
[-5.756, 0],
[0, 0],
[0, 5.756],
[0, 0],
[5.747, 0]
],
"o": [
[-5.756, 0],
[0, 0],
[0, 5.756],
[0, 0],
[5.747, 0],
[0, 0],
[0, -5.757],
[0, 0]
],
"v": [
[-41.732, -31.294],
[-52.165, -20.86],
[-52.165, 20.87],
[-41.732, 31.303],
[41.73, 31.303],
[52.163, 20.87],
[52.163, -20.86],
[41.73, -31.294]
],
"c": true
},
"ix": 2
},
"nm": "Path 1",
"mn": "ADBE Vector Shape - Group",
"hd": false
},
{
"ind": 1,
"ty": "sh",
"ix": 2,
"ks": {
"a": 0,
"k": {
"i": [
[23.006, 0],
[0, 0],
[0, 23.015],
[0, 0],
[-23.015, 0],
[0, 0],
[0, -23.016],
[0, 0]
],
"o": [
[0, 0],
[-23.015, 0],
[0, 0],
[0, -23.016],
[0, 0],
[23.006, 0],
[0, 0],
[0, 23.015]
],
"v": [
[41.73, 62.592],
[-41.732, 62.592],
[-83.463, 20.87],
[-83.463, -20.86],
[-41.732, -62.592],
[41.73, -62.592],
[83.463, -20.86],
[83.463, 20.87]
],
"c": true
},
"ix": 2
},
"nm": "Path 2",
"mn": "ADBE Vector Shape - Group",
"hd": false
},
{
"ty": "mm",
"mm": 1,
"nm": "Merge Paths 1",
"mn": "ADBE Vector Filter - Merge",
"hd": false
},
{
"ty": "fl",
"c": {
"a": 0,
"k": [0.466666696586, 0.458823559331, 0.490196108351, 1],
"ix": 4
},
"o": { "a": 0, "k": 100, "ix": 5 },
"r": 1,
"bm": 0,
"nm": "Fill 1",
"mn": "ADBE Vector Graphic - Fill",
"hd": false
},
{
"ty": "tr",
"p": {
"a": 1,
"k": [
{
"i": { "x": 0.833, "y": 0.833 },
"o": { "x": 0.167, "y": 0.167 },
"t": 0,
"s": [182.824, 141.088],
"to": [0, 0],
"ti": [0, 0]
},
{
"i": { "x": 0.833, "y": 0.833 },
"o": { "x": 0.167, "y": 0.167 },
"t": 30,
"s": [152.824, 131.088],
"to": [0, 0],
"ti": [0, 0]
},
{ "t": 58.0000023623884, "s": [182.824, 141.088] }
],
"ix": 2
},
"a": { "a": 0, "k": [0, 0], "ix": 1 },
"s": { "a": 0, "k": [100, 100], "ix": 3 },
"r": { "a": 0, "k": 0, "ix": 6 },
"o": { "a": 0, "k": 100, "ix": 7 },
"sk": { "a": 0, "k": 0, "ix": 4 },
"sa": { "a": 0, "k": 0, "ix": 5 },
"nm": "Transform"
}
],
"nm": "Group 4",
"np": 4,
"cix": 2,
"bm": 0,
"ix": 4,
"mn": "ADBE Vector Group",
"hd": false
}
],
"ip": 0,
"op": 60.0000024438501,
"st": 0,
"bm": 0
}
],
"markers": []
}

View File

@@ -0,0 +1,177 @@
import { useTranslation } from '@affine/i18n';
import { CloseIcon } from '@blocksuite/icons';
import type { Page } from '@blocksuite/store';
import type { HTMLAttributes, PropsWithChildren } from 'react';
import type React from 'react';
import { forwardRef, useEffect, useMemo, useState } from 'react';
import {
useSidebarFloating,
useSidebarStatus,
} from '../../../hooks/use-sidebar-status';
import type { AffineOfficialWorkspace } from '../../../shared';
import { SidebarSwitch } from '../../affine/sidebar-switch';
import { EditorOptionMenu } from './header-right-items/EditorOptionMenu';
import SyncUser from './header-right-items/SyncUser';
import ThemeModeSwitch from './header-right-items/theme-mode-switch';
import TrashButtonGroup from './header-right-items/TrashButtonGroup';
import {
StyledBrowserWarning,
StyledCloseButton,
StyledHeader,
StyledHeaderContainer,
StyledHeaderRightSide,
} from './styles';
import { OSWarningMessage, shouldShowWarning } from './utils';
const BrowserWarning = ({
show,
onClose,
}: {
show: boolean;
onClose: () => void;
}) => {
return (
<StyledBrowserWarning show={show}>
<OSWarningMessage />
<StyledCloseButton onClick={onClose}>
<CloseIcon />
</StyledCloseButton>
</StyledBrowserWarning>
);
};
export type BaseHeaderProps<
Workspace extends AffineOfficialWorkspace = AffineOfficialWorkspace
> = {
workspace: Workspace;
currentPage: Page | null;
isPublic: boolean;
isPreview: boolean;
};
export const enum HeaderRightItemName {
EditorOptionMenu = 'editorOptionMenu',
TrashButtonGroup = 'trashButtonGroup',
ThemeModeSwitch = 'themeModeSwitch',
SyncUser = 'syncUser',
ShareMenu = 'shareMenu',
}
type HeaderItem = {
Component: React.FC<BaseHeaderProps>;
// todo: public workspace should be one of the flavour
availableWhen: (
workspace: AffineOfficialWorkspace,
currentPage: Page | null,
status: {
isPublic: boolean;
isPreview: boolean;
}
) => boolean;
};
const HeaderRightItems: Record<HeaderRightItemName, HeaderItem> = {
[HeaderRightItemName.TrashButtonGroup]: {
Component: TrashButtonGroup,
availableWhen: (_, currentPage) => {
return currentPage?.meta.trash === true;
},
},
[HeaderRightItemName.SyncUser]: {
Component: SyncUser,
availableWhen: (_, currentPage, { isPublic, isPreview }) => {
return !isPublic && !isPreview;
},
},
[HeaderRightItemName.ThemeModeSwitch]: {
Component: ThemeModeSwitch,
availableWhen: (_, currentPage) => {
return currentPage?.meta.trash !== true;
},
},
[HeaderRightItemName.EditorOptionMenu]: {
Component: EditorOptionMenu,
availableWhen: (_, currentPage, { isPublic, isPreview }) => {
return !!currentPage && !isPublic && !isPreview;
},
},
[HeaderRightItemName.ShareMenu]: {
Component: () => null,
availableWhen: (_, currentPage, { isPublic, isPreview }) => {
return false;
},
},
};
export type HeaderProps = BaseHeaderProps;
export const Header = forwardRef<
HTMLDivElement,
PropsWithChildren<HeaderProps> & HTMLAttributes<HTMLDivElement>
>((props, ref) => {
const [showWarning, setShowWarning] = useState(false);
useEffect(() => {
setShowWarning(shouldShowWarning());
}, []);
const [open] = useSidebarStatus();
const sidebarFloating = useSidebarFloating();
const { t } = useTranslation();
return (
<StyledHeaderContainer
sidebarFloating={sidebarFloating && open}
ref={ref}
hasWarning={showWarning}
{...props}
>
<BrowserWarning
show={showWarning}
onClose={() => {
setShowWarning(false);
}}
/>
<StyledHeader
hasWarning={showWarning}
data-testid="editor-header-items"
data-tauri-drag-region
>
<SidebarSwitch
visible={!open}
tooltipContent={t('Expand sidebar')}
testid="sliderBar-arrowButton-expand"
/>
{props.children}
<StyledHeaderRightSide>
{useMemo(() => {
return Object.entries(HeaderRightItems).map(
([name, { availableWhen, Component }]) => {
if (
availableWhen(props.workspace, props.currentPage, {
isPreview: props.isPreview,
isPublic: props.isPublic,
})
) {
return (
<Component
workspace={props.workspace}
currentPage={props.currentPage}
isPreview={props.isPreview}
isPublic={props.isPublic}
key={name}
/>
);
}
return null;
}
);
}, [props])}
{/*<ShareMenu />*/}
</StyledHeaderRightSide>
</StyledHeader>
</StyledHeaderContainer>
);
});
Header.displayName = 'Header';

View File

@@ -0,0 +1,128 @@
import type { PopperProps } from '@affine/component';
import { QuickSearchTips } from '@affine/component';
import { getEnvironment } from '@affine/env';
import { ArrowDownSmallIcon } from '@blocksuite/icons';
import { assertExists } from '@blocksuite/store';
import { useAtomValue, useSetAtom } from 'jotai';
import type { HTMLAttributes, PropsWithChildren } from 'react';
import { forwardRef, useCallback, useRef } from 'react';
import { currentEditorAtom, openQuickSearchModalAtom } from '../../../atoms';
import { useGuideHidden } from '../../../hooks/use-is-first-load';
import { usePageMeta } from '../../../hooks/use-page-meta';
import { useElementResizeEffect } from '../../../hooks/use-workspaces';
import { QuickSearchButton } from '../../pure/quick-search-button';
import { EditorModeSwitch } from './editor-mode-switch';
import type { BaseHeaderProps } from './header';
import { Header } from './header';
import {
StyledQuickSearchTipButton,
StyledQuickSearchTipContent,
StyledSearchArrowWrapper,
StyledSwitchWrapper,
StyledTitle,
StyledTitleContainer,
StyledTitleWrapper,
} from './styles';
export type WorkspaceHeaderProps = BaseHeaderProps;
export const WorkspaceHeader = forwardRef<
HTMLDivElement,
PropsWithChildren<WorkspaceHeaderProps> & HTMLAttributes<HTMLDivElement>
>((props, ref) => {
const { workspace, currentPage, children, isPublic } = props;
// fixme(himself65): remove this atom and move it to props
const setOpenQuickSearch = useSetAtom(openQuickSearchModalAtom);
const pageMeta = usePageMeta(workspace.blockSuiteWorkspace).find(
meta => meta.id === currentPage?.id
);
assertExists(pageMeta);
const title = pageMeta.title;
const [isTipsHidden, setTipsHidden] = useGuideHidden();
const isMac = () => {
const env = getEnvironment();
return env.isBrowser && env.isMacOs;
};
const popperRef: PopperProps['popperRef'] = useRef(null);
useElementResizeEffect(
useAtomValue(currentEditorAtom),
useCallback(() => {
if (isTipsHidden.quickSearchTips || !popperRef.current) {
return;
}
popperRef.current.update();
}, [isTipsHidden.quickSearchTips])
);
const TipsContent = (
<StyledQuickSearchTipContent>
<div>
Click button
{
<span
style={{
fontSize: '24px',
verticalAlign: 'middle',
}}
>
<ArrowDownSmallIcon />
</span>
}
or use
{isMac() ? ' ⌘ + K' : ' Ctrl + K'} to activate Quick Search. Then you
can search keywords or quickly open recently viewed pages.
</div>
<StyledQuickSearchTipButton
data-testid="quick-search-got-it"
onClick={() =>
setTipsHidden({ ...isTipsHidden, quickSearchTips: true })
}
>
Got it
</StyledQuickSearchTipButton>
</StyledQuickSearchTipContent>
);
return (
<Header ref={ref} {...props}>
{children}
{!isPublic && currentPage && (
<StyledTitleContainer data-tauri-drag-region>
<StyledTitleWrapper>
<StyledSwitchWrapper>
<EditorModeSwitch
blockSuiteWorkspace={workspace.blockSuiteWorkspace}
pageId={currentPage.id}
style={{
marginRight: '12px',
}}
/>
</StyledSwitchWrapper>
<StyledTitle>{title || 'Untitled'}</StyledTitle>
<QuickSearchTips
data-testid="quick-search-tips"
content={TipsContent}
placement="bottom"
popperRef={popperRef}
open={!isTipsHidden.quickSearchTips}
offset={[0, -5]}
>
<StyledSearchArrowWrapper>
<QuickSearchButton
onClick={() => {
setOpenQuickSearch(true);
}}
/>
</StyledSearchArrowWrapper>
</QuickSearchTips>
</StyledTitleWrapper>
</StyledTitleContainer>
)}
</Header>
);
});
WorkspaceHeader.displayName = 'BlockSuiteEditorHeader';

View File

@@ -10,14 +10,14 @@ import { useCallback } from 'react';
import { currentEditorAtom, workspacePreferredModeAtom } from '../atoms';
import { usePageMeta } from '../hooks/use-page-meta';
import type { BlockSuiteWorkspace } from '../shared';
import type { AffineOfficialWorkspace } from '../shared';
import { PageNotFoundError } from './affine/affine-error-eoundary';
import { BlockSuiteEditorHeader } from './blocksuite/header';
import { WorkspaceHeader } from './blocksuite/workspace-header';
export type PageDetailEditorProps = {
isPublic?: boolean;
isPreview?: boolean;
blockSuiteWorkspace: BlockSuiteWorkspace;
workspace: AffineOfficialWorkspace;
pageId: string;
onInit: (page: Page, editor: Readonly<EditorContainer>) => void;
onLoad?: (page: Page, editor: EditorContainer) => void;
@@ -33,7 +33,7 @@ const Editor = dynamic(
);
export const PageDetailEditor: React.FC<PageDetailEditorProps> = ({
blockSuiteWorkspace,
workspace,
pageId,
onInit,
onLoad,
@@ -41,6 +41,7 @@ export const PageDetailEditor: React.FC<PageDetailEditorProps> = ({
isPublic,
isPreview,
}) => {
const blockSuiteWorkspace = workspace.blockSuiteWorkspace;
const page = blockSuiteWorkspace.getPage(pageId);
if (!page) {
throw new PageNotFoundError(blockSuiteWorkspace, pageId);
@@ -58,14 +59,14 @@ export const PageDetailEditor: React.FC<PageDetailEditorProps> = ({
<Head>
<title>{title}</title>
</Head>
<BlockSuiteEditorHeader
isPublic={isPublic}
isPreview={isPreview}
blockSuiteWorkspace={blockSuiteWorkspace}
pageId={pageId}
<WorkspaceHeader
isPublic={isPublic ?? false}
isPreview={isPreview ?? false}
workspace={workspace}
currentPage={page}
>
{header}
</BlockSuiteEditorHeader>
</WorkspaceHeader>
<Editor
style={{
height: 'calc(100% - 52px)',

View File

@@ -7,6 +7,8 @@ import {
import { WorkspaceList } from '@affine/component/workspace-list';
import { useTranslation } from '@affine/i18n';
import type { AccessTokenMessage } from '@affine/workspace/affine/login';
import type { AffineWorkspace, LocalWorkspace } from '@affine/workspace/type';
import { WorkspaceFlavour } from '@affine/workspace/type';
import { HelpIcon, PlusIcon } from '@blocksuite/icons';
import type { DragEndEvent } from '@dnd-kit/core';
import { useCallback } from 'react';
@@ -99,7 +101,11 @@ export const WorkspaceListModal = ({
<StyledModalContent>
<WorkspaceList
disabled={disabled}
items={workspaces}
items={
workspaces.filter(
({ flavour }) => flavour !== WorkspaceFlavour.PUBLIC
) as (AffineWorkspace | LocalWorkspace)[]
}
currentWorkspaceId={currentWorkspaceId}
onClick={onClickWorkspace}
onSettingClick={onClickWorkspaceSetting}

View File

@@ -3,21 +3,25 @@ import type { ReactNode } from 'react';
import type React from 'react';
import { openQuickSearchModalAtom } from '../../../atoms';
import Header from '../../blocksuite/header/header';
import { StyledPageListTittleWrapper } from '../../blocksuite/header/styles';
import type { HeaderProps } from '../../blocksuite/workspace-header/header';
import { Header } from '../../blocksuite/workspace-header/header';
import { StyledPageListTittleWrapper } from '../../blocksuite/workspace-header/styles';
import { QuickSearchButton } from '../quick-search-button';
export type WorkspaceTitleProps = React.PropsWithChildren<{
icon?: ReactNode;
}>;
export type WorkspaceTitleProps = React.PropsWithChildren<
HeaderProps & {
icon?: ReactNode;
}
>;
export const WorkspaceTitle: React.FC<WorkspaceTitleProps> = ({
icon,
children,
...props
}) => {
const setOpenQuickSearch = useSetAtom(openQuickSearchModalAtom);
return (
<Header>
<Header {...props}>
<StyledPageListTittleWrapper>
{icon}
{children}