Merge branch 'feat/fileSystem' into develop

This commit is contained in:
QiShaoXuan
2022-12-16 16:34:24 +08:00
32 changed files with 439 additions and 223 deletions
+3 -3
View File
@@ -9,10 +9,10 @@
"lint": "next lint"
},
"dependencies": {
"@blocksuite/blocks": "0.3.0-alpha.8",
"@blocksuite/editor": "0.3.0-alpha.8",
"@blocksuite/blocks": "0.3.0-alpha.9",
"@blocksuite/editor": "0.3.0-alpha.9",
"@blocksuite/icons": "^2.0.0",
"@blocksuite/store": "0.3.0-alpha.8",
"@blocksuite/store": "0.3.0-alpha.9",
"@emotion/css": "^11.10.0",
"@emotion/react": "^11.10.4",
"@emotion/server": "^11.10.0",
@@ -66,7 +66,9 @@ export const EditorModeSwitch = ({
style = {},
}: AnimateRadioProps) => {
const { mode: themeMode } = useTheme();
const { mode, setMode } = useEditor();
const { mode, setMode, getPageMeta } = useEditor();
const pageMeta = getPageMeta();
const modifyRadioItemStatus = (): RadioItemStatus => {
return {
left: isHover
@@ -99,6 +101,7 @@ export const EditorModeSwitch = ({
data-testid="editor-mode-switcher"
shrink={!isHover}
style={style}
disabled={!!pageMeta?.trash}
>
<AnimateRadioItem
isLeft={true}
@@ -5,41 +5,42 @@ import type { ItemStatus } from './type';
const ANIMATE_DURATION = 500;
export const StyledAnimateRadioContainer = styled('div')<{ shrink: boolean }>(
({ shrink, theme }) => {
const animateScaleStretch = keyframes`${toString(
spring({ width: '36px' }, { width: '160px' }, { preset: 'gentle' })
)}`;
const animateScaleShrink = keyframes(
`${toString(
spring({ width: '160px' }, { width: '36px' }, { preset: 'gentle' })
)}`
);
const shrinkStyle = shrink
? {
animation: `${animateScaleShrink} ${ANIMATE_DURATION}ms forwards`,
background: 'transparent',
}
: {
animation: `${animateScaleStretch} ${ANIMATE_DURATION}ms forwards`,
};
export const StyledAnimateRadioContainer = styled('div')<{
shrink: boolean;
disabled: boolean;
}>(({ shrink, theme, disabled }) => {
const animateScaleStretch = keyframes`${toString(
spring({ width: '36px' }, { width: '160px' }, { preset: 'gentle' })
)}`;
const animateScaleShrink = keyframes(
`${toString(
spring({ width: '160px' }, { width: '36px' }, { preset: 'gentle' })
)}`
);
const shrinkStyle = shrink
? {
animation: `${animateScaleShrink} ${ANIMATE_DURATION}ms forwards`,
background: 'transparent',
}
: {
animation: `${animateScaleStretch} ${ANIMATE_DURATION}ms forwards`,
};
return {
height: '36px',
borderRadius: '18px',
background: theme.colors.hoverBackground,
position: 'relative',
display: 'flex',
transition: `background ${ANIMATE_DURATION}ms, border ${ANIMATE_DURATION}ms`,
border: '1px solid transparent',
return {
height: '36px',
borderRadius: '18px',
background: disabled ? 'transparent' : theme.colors.hoverBackground,
position: 'relative',
display: 'flex',
transition: `background ${ANIMATE_DURATION}ms, border ${ANIMATE_DURATION}ms`,
border: '1px solid transparent',
...shrinkStyle,
':hover': {
border: `1px solid ${theme.colors.primaryColor}`,
},
};
}
);
...(disabled ? { pointerEvents: 'none' } : shrinkStyle),
':hover': {
border: disabled ? '' : `1px solid ${theme.colors.primaryColor}`,
},
};
});
export const StyledMiddleLine = styled('div')<{
hidden: boolean;
-76
View File
@@ -1,76 +0,0 @@
import { useState } from 'react';
import {
StyledFAQ,
StyledIconWrapper,
StyledFAQWrapper,
StyledTransformIcon,
} from './style';
import { CloseIcon, ContactIcon, HelpIcon, KeyboardIcon } from './icons';
import Grow from '@mui/material/Grow';
import { Tooltip } from '@/ui/tooltip';
import { useEditor } from '@/providers/editor-provider';
import { useModal } from '@/providers/global-modal-provider';
import { useTheme } from '@/providers/themeProvider';
export const FAQ = () => {
const [showContent, setShowContent] = useState(false);
const { mode } = useTheme();
const { mode: editorMode } = useEditor();
const { triggerShortcutsModal, triggerContactModal } = useModal();
const isEdgelessDark = mode === 'dark' && editorMode === 'edgeless';
return (
<>
<StyledFAQ
className=""
onMouseEnter={() => {
setShowContent(true);
}}
onMouseLeave={() => {
setShowContent(false);
}}
>
<Grow in={showContent}>
<StyledFAQWrapper>
<Tooltip content="Contact Us" placement="left-end">
<StyledIconWrapper
data-testid="right-bottom-contact-us-icon"
isEdgelessDark={isEdgelessDark}
onClick={() => {
setShowContent(false);
triggerContactModal();
}}
>
<ContactIcon />
</StyledIconWrapper>
</Tooltip>
<Tooltip content="Keyboard Shortcuts" placement="left-end">
<StyledIconWrapper
data-testid="shortcuts-icon"
isEdgelessDark={isEdgelessDark}
onClick={() => {
setShowContent(false);
triggerShortcutsModal();
}}
>
<KeyboardIcon />
</StyledIconWrapper>
</Tooltip>
</StyledFAQWrapper>
</Grow>
<div style={{ position: 'relative' }}>
<StyledIconWrapper
data-testid="faq-icon"
isEdgelessDark={isEdgelessDark}
>
<HelpIcon />
</StyledIconWrapper>
<StyledTransformIcon in={showContent}>
<CloseIcon />
</StyledTransformIcon>
</div>
</StyledFAQ>
</>
);
};
@@ -5,25 +5,21 @@ import {
StyledTitle,
StyledTitleWrapper,
} from './styles';
import { IconButton } from '@/ui/button';
import { Content } from '@/ui/layout';
import { useEditor } from '@/providers/editor-provider';
import EditorModeSwitch from '@/components/editor-mode-switch';
import { ArrowDownIcon } from '@blocksuite/icons';
import { useModal } from '@/providers/global-modal-provider';
import QuickSearchButton from './quick-search-button';
import Header from './header';
export const PageHeader = () => {
export const EditorHeader = () => {
const [title, setTitle] = useState('');
const [isHover, setIsHover] = useState(false);
const { editor, onPropsUpdated } = useEditor();
const { triggerQuickSearchModal } = useModal();
const { editor, onPropsUpdated, getPageMeta } = useEditor();
useEffect(() => {
onPropsUpdated(editor => {
setTitle(editor.model.title);
setTitle(editor.model.title || 'Untitled');
});
}, [onPropsUpdated]);
@@ -31,14 +27,20 @@ export const PageHeader = () => {
setTitle(editor?.model.title || 'Untitled');
}, [editor]);
const pageMeta = getPageMeta();
return (
<Header>
{title && (
<StyledTitle
onMouseEnter={() => {
if (pageMeta?.trash) return;
setIsHover(true);
}}
onMouseLeave={() => {
if (pageMeta?.trash) return;
setIsHover(false);
}}
>
@@ -53,13 +55,7 @@ export const PageHeader = () => {
</StyledSwitchWrapper>
<Content ellipsis={true}>{title}</Content>
<StyledSearchArrowWrapper>
<IconButton
onClick={() => {
triggerQuickSearchModal();
}}
>
<ArrowDownIcon />
</IconButton>
<QuickSearchButton />
</StyledSearchArrowWrapper>
</StyledTitleWrapper>
</StyledTitle>
@@ -68,4 +64,4 @@ export const PageHeader = () => {
);
};
export default PageHeader;
export default EditorHeader;
+2 -2
View File
@@ -1,3 +1,3 @@
export * from './header';
export * from './page-header';
export { StyledTitle as HeaderWrapper } from './styles';
export * from './editor-header';
export * from './page-list-header';
@@ -0,0 +1,21 @@
import { PropsWithChildren, ReactNode } from 'react';
import Header from './header';
import { StyledPageListTittleWrapper } from './styles';
import QuickSearchButton from './quick-search-button';
export type PageListHeaderProps = PropsWithChildren<{
icon?: ReactNode;
}>;
export const PageListHeader = ({ icon, children }: PageListHeaderProps) => {
return (
<Header>
<StyledPageListTittleWrapper>
{icon}
{children}
<QuickSearchButton style={{ marginLeft: '5px' }} />
</StyledPageListTittleWrapper>
</Header>
);
};
export default PageListHeader;
@@ -0,0 +1,25 @@
import React from 'react';
import { IconButton, IconButtonProps } from '@/ui/button';
import { ArrowDownIcon } from '@blocksuite/icons';
import { useModal } from '@/providers/global-modal-provider';
export const QuickSearchButton = ({
onClick,
...props
}: Omit<IconButtonProps, 'children'>) => {
const { triggerQuickSearchModal } = useModal();
return (
<IconButton
{...props}
onClick={e => {
onClick?.(e);
triggerQuickSearchModal();
}}
>
<ArrowDownIcon />
</IconButton>
);
};
export default QuickSearchButton;
@@ -110,3 +110,14 @@ export const StyledSearchArrowWrapper = styled.div(() => {
...displayFlex('center', 'center'),
};
});
export const StyledPageListTittleWrapper = styled(StyledTitle)(({ theme }) => {
return {
fontSize: theme.font.sm,
color: theme.colors.textColor,
'>svg': {
fontSize: '20px',
marginRight: '12px',
},
};
});
@@ -0,0 +1,86 @@
import { useState } from 'react';
import {
StyledIsland,
StyledIconWrapper,
StyledIslandWrapper,
StyledTransformIcon,
} from './style';
import { CloseIcon, ContactIcon, HelpIcon, KeyboardIcon } from './icons';
import Grow from '@mui/material/Grow';
import { Tooltip } from '@/ui/tooltip';
import { useEditor } from '@/providers/editor-provider';
import { useModal } from '@/providers/global-modal-provider';
import { useTheme } from '@/providers/themeProvider';
export type IslandItemNames = 'contact' | 'shortcuts';
// export type IslandShowMap = Record<IslandItemNames, boolean>;
export const HelpIsland = ({
showList = ['contact', 'shortcuts'],
}: {
showList?: IslandItemNames[];
}) => {
const [showContent, setShowContent] = useState(false);
const { mode } = useTheme();
const { mode: editorMode } = useEditor();
const { triggerShortcutsModal, triggerContactModal } = useModal();
const isEdgelessDark = mode === 'dark' && editorMode === 'edgeless';
return (
<>
<StyledIsland
className=""
onMouseEnter={() => {
setShowContent(true);
}}
onMouseLeave={() => {
setShowContent(false);
}}
>
<Grow in={showContent}>
<StyledIslandWrapper>
{showList.includes('contact') && (
<Tooltip content="Contact Us" placement="left-end">
<StyledIconWrapper
data-testid="right-bottom-contact-us-icon"
isEdgelessDark={isEdgelessDark}
onClick={() => {
setShowContent(false);
triggerContactModal();
}}
>
<ContactIcon />
</StyledIconWrapper>
</Tooltip>
)}
{showList.includes('shortcuts') && (
<Tooltip content="Keyboard Shortcuts" placement="left-end">
<StyledIconWrapper
data-testid="shortcuts-icon"
isEdgelessDark={isEdgelessDark}
onClick={() => {
setShowContent(false);
triggerShortcutsModal();
}}
>
<KeyboardIcon />
</StyledIconWrapper>
</Tooltip>
)}
</StyledIslandWrapper>
</Grow>
<div style={{ position: 'relative' }}>
<StyledIconWrapper isEdgelessDark={isEdgelessDark}>
<HelpIcon />
</StyledIconWrapper>
<StyledTransformIcon in={showContent}>
<CloseIcon />
</StyledTransformIcon>
</div>
</StyledIsland>
</>
);
};
export default HelpIsland;
@@ -1,6 +1,6 @@
import { displayFlex, styled } from '@/styles';
export const StyledFAQ = styled('div')(({ theme }) => {
export const StyledIsland = styled('div')(({ theme }) => {
return {
width: '32px',
height: '32px',
@@ -57,7 +57,7 @@ export const StyledIconWrapper = styled('div')<{ isEdgelessDark: boolean }>(
}
);
export const StyledFAQWrapper = styled('div')(({ theme }) => {
export const StyledIslandWrapper = styled('div')(({ theme }) => {
return {
position: 'absolute',
bottom: '100%',
@@ -9,16 +9,17 @@ dayjs.extend(localizedFormat);
export const DateCell = ({
pageMeta,
dateKey,
backupKey = '',
}: {
pageMeta: PageMeta;
dateKey: keyof PageMeta;
backupKey?: keyof PageMeta;
}) => {
// dayjs().format('L LT');
const value = pageMeta[dateKey] ?? pageMeta[backupKey];
return (
<TableCell ellipsis={true}>
{pageMeta[dateKey] === undefined
? '--'
: dayjs(pageMeta[dateKey] as string).format('YYYY-MM-DD HH:mm')}
{value ? dayjs(value as string).format('YYYY-MM-DD HH:mm') : '--'}
</TableCell>
);
};
@@ -85,6 +85,7 @@ export const PageList = ({
<DateCell
pageMeta={pageMeta}
dateKey={isTrash ? 'trashDate' : 'updatedDate'}
backupKey={isTrash ? 'trashDate' : 'createDate'}
/>
<TableCell style={{ padding: 0 }}>
{isTrash ? (
@@ -40,9 +40,8 @@ export const OperationCell = ({ pageMeta }: { pageMeta: PageMeta }) => {
<MenuItem
onClick={() => {
confirm({
title: 'Delete',
content:
'Deleted items will be moved to Trash Bin. Do you confirm?',
title: 'Delete page?',
content: `${pageMeta.title} will be moved to Trash`,
confirmText: 'Delete',
confirmType: 'danger',
}).then(confirm => {
+104
View File
@@ -0,0 +1,104 @@
// Copyright: https://github.com/toeverything/blocksuite/commit/8032ef3ab97aefce01664b36502fc392c5db8b78#diff-bf5b41be21936f9165a8400c7f20e24d3dbc49644ba57b9258e0943f0dc1c464
import { css, html, TemplateResult } from 'lit';
export const sleep = (ms = 0) =>
new Promise(resolve => setTimeout(resolve, ms));
let ToastContainer: HTMLDivElement | null = null;
/**
* DO NOT USE FOR USER INPUT
* See https://stackoverflow.com/questions/494143/creating-a-new-dom-element-from-an-html-string-using-built-in-dom-methods-or-pro/35385518#35385518
*/
const htmlToElement = <T extends ChildNode>(html: string | TemplateResult) => {
const template = document.createElement('template');
if (typeof html === 'string') {
html = html.trim(); // Never return a text node of whitespace as the result
template.innerHTML = html;
} else {
const { strings, values } = html;
const v = [...values, '']; // + last emtpty part
template.innerHTML = strings.reduce((acc, cur, i) => acc + cur + v[i], '');
}
return template.content.firstChild as T;
};
const createToastContainer = () => {
const styles = css`
position: fixed;
z-index: 9999;
top: 16px;
left: 16px;
right: 16px;
bottom: 78px;
pointer-events: none;
display: flex;
flex-direction: column-reverse;
align-items: center;
`;
const template = html`<div style="${styles}"></div>`;
const element = htmlToElement<HTMLDivElement>(template);
document.body.appendChild(element);
return element;
};
/**
* @example
* ```ts
* toast('Hello World');
* ```
*/
export const toast = (message: string, duration = 2500) => {
if (!ToastContainer) {
ToastContainer = createToastContainer();
}
const styles = css`
max-width: 480px;
text-align: center;
font-family: var(--affine-font-family);
font-size: var(--affine-font-sm);
padding: 6px 12px;
margin: 10px 0 0 0;
color: var(--affine-tooltip-color);
background: var(--affine-tooltip-background);
box-shadow: var(--affine-tooltip-shadow);
border-radius: 10px;
transition: all 230ms cubic-bezier(0.21, 1.02, 0.73, 1);
opacity: 0;
`;
const template = html`<div style="${styles}"></div>`;
const element = htmlToElement<HTMLDivElement>(template);
// message is not trusted
element.textContent = message;
ToastContainer.appendChild(element);
const fadeIn = [
{
opacity: 0,
},
{ opacity: 1 },
];
const options = {
duration: 230,
easing: 'cubic-bezier(0.21, 1.02, 0.73, 1)',
fill: 'forwards' as const,
}; // satisfies KeyframeAnimationOptions;
element.animate(fadeIn, options);
setTimeout(async () => {
const fadeOut = fadeIn.reverse();
const animation = element.animate(fadeOut, options);
await animation.finished;
element.style.maxHeight = '0';
element.style.margin = '0';
element.style.padding = '0';
// wait for transition
await sleep(230);
element.remove();
}, duration);
return element;
};
export default toast;
@@ -20,10 +20,12 @@ import {
AddIcon,
} from '@blocksuite/icons';
import Link from 'next/link';
import { Tooltip } from '@/ui/tooltip';
import { useEditor } from '@/providers/editor-provider';
import { useModal } from '@/providers/global-modal-provider';
import { IconButton } from '@/ui/button';
import useLocalStorage from '@/hooks/use-local-storage';
const FavoriteList = ({ showList }: { showList: boolean }) => {
const { pageList, openPage } = useEditor();
@@ -45,7 +47,7 @@ const FavoriteList = ({ showList }: { showList: boolean }) => {
openPage(pageMeta.id);
}}
>
{pageMeta.title || pageMeta.id}
{pageMeta.title || 'Untitled'}
</StyledSubListItem>
);
})}
@@ -57,11 +59,13 @@ const FavoriteList = ({ showList }: { showList: boolean }) => {
};
export const WorkSpaceSliderBar = () => {
const { triggerQuickSearchModal, triggerImportModal } = useModal();
const [show, setShow] = useState(false);
const [showSubFavorite, setShowSubFavorite] = useState(true);
const { createPage, getPageMeta, openPage } = useEditor();
const router = useRouter();
const [showTip, setShowTip] = useState(false);
const [show, setShow] = useLocalStorage('AFFINE_SLIDE_BAR', false, true);
return (
<>
<StyledSliderBar show={show}>
@@ -120,14 +124,27 @@ export const WorkSpaceSliderBar = () => {
<AddIcon /> New Page
</StyledNewPageButton>
</StyledSliderBar>
<StyledArrowButton
isShow={show}
onClick={() => {
setShow(!show);
}}
<Tooltip
content={show ? 'Collapse sidebar' : 'Expand sidebar'}
placement="right"
visible={showTip}
>
<Arrow />
</StyledArrowButton>
<StyledArrowButton
isShow={show}
onClick={() => {
setShow(!show);
setShowTip(false);
}}
onMouseEnter={() => {
setShowTip(true);
}}
onMouseLeave={() => {
setShowTip(false);
}}
>
<Arrow />
</StyledArrowButton>
</Tooltip>
</>
);
};
@@ -0,0 +1,33 @@
import { Dispatch, SetStateAction, useEffect, useState } from 'react';
export type UseLocalStorage = <S>(
key: string,
initialState: S | (() => S),
// If initialState is different from the initial local data, set it
initialValue?: S
) => [S, Dispatch<SetStateAction<S>>];
export const useLocalStorage: UseLocalStorage = (
key,
defaultValue,
initialValue
) => {
const [item, setItem] = useState(defaultValue);
useEffect(() => {
const saved = localStorage.getItem(key);
if (saved) {
setItem(JSON.parse(saved));
} else if (initialValue) {
setItem(initialValue);
}
}, [initialValue, key, setItem]);
useEffect(() => {
localStorage.setItem(key, JSON.stringify(item));
}, [item, key]);
return [item, setItem];
};
export default useLocalStorage;
+8
View File
@@ -13,6 +13,9 @@ import '../utils/print-build-info';
import { styled } from '@/styles';
import ProviderComposer from '@/components/provider-composer';
import ConfirmProvider from '@/providers/confirm-provider';
import HelpIsland from '@/components/help-island';
import { useRouter } from 'next/router';
const ThemeProvider = dynamic(() => import('@/providers/themeProvider'), {
ssr: false,
});
@@ -35,6 +38,8 @@ const StyledWrapper = styled('div')(({ theme }) => {
});
function MyApp({ Component, pageProps }: AppProps) {
const router = useRouter();
return (
<>
<Logger />
@@ -50,6 +55,9 @@ function MyApp({ Component, pageProps }: AppProps) {
<WorkSpaceSliderBar />
<StyledWrapper>
<Component {...pageProps} />
<HelpIsland
showList={router.pathname !== '/' ? ['contact'] : undefined}
/>
</StyledWrapper>
</StyledPage>
</ProviderComposer>
+9 -1
View File
@@ -5,6 +5,7 @@ import Modal from '@/ui/modal';
import { useState } from 'react';
import { Button } from '@/ui/button';
import { FavouritedIcon } from '@blocksuite/icons';
import { toast } from '@/components/toast';
export const StyledHeader = styled('div')({
height: '60px',
width: '100vw',
@@ -38,7 +39,14 @@ const Affine = () => {
</Modal>
<Loading />
<Button icon={<FavouritedIcon />}>click me!</Button>
<Button
icon={<FavouritedIcon />}
onClick={() => {
toast('hello, world!!');
}}
>
click me!
</Button>
<Button icon={<FavouritedIcon />} type={'primary'}>
click me!
</Button>
+2 -4
View File
@@ -1,7 +1,6 @@
import type { NextPage } from 'next';
import { styled } from '@/styles';
import { PageHeader } from '@/components/header';
import { FAQ } from '@/components/faq';
import { EditorHeader } from '@/components/header';
import EdgelessToolbar from '@/components/edgeless-toolbar';
import MobileModal from '@/components/mobile-modal';
import Editor from '@/components/editor';
@@ -15,12 +14,11 @@ const StyledEditorContainer = styled('div')(({ theme }) => {
const Home: NextPage = () => {
return (
<>
<PageHeader />
<EditorHeader />
<MobileModal />
<StyledEditorContainer>
<Editor />
</StyledEditorContainer>
<FAQ />
<EdgelessToolbar />
</>
);
+2 -20
View File
@@ -1,32 +1,14 @@
import { Header } from '@/components/header';
import { useEditor } from '@/providers/editor-provider';
import { PageList } from '@/components/page-list';
import { AllPagesIcon } from '@blocksuite/icons';
import { HeaderWrapper } from '@/components/header';
import { styled } from '@/styles';
export const StyledWrapper = styled(HeaderWrapper)(({ theme }) => {
return {
fontSize: theme.font.sm,
color: theme.colors.textColor,
'>svg': {
fontSize: '20px',
marginRight: '12px',
},
};
});
import { PageListHeader } from '@/components/header';
export const All = () => {
const { pageList: allPageList } = useEditor();
return (
<>
<Header>
<StyledWrapper>
<AllPagesIcon />
All Pages
</StyledWrapper>
</Header>
<PageListHeader icon={<AllPagesIcon />}>All Page</PageListHeader>
<PageList
pageList={allPageList.filter(p => !p.trash)}
showFavoriteTag={true}
@@ -1,19 +1,13 @@
import { Header } from '@/components/header';
import { PageListHeader } from '@/components/header';
import { useEditor } from '@/providers/editor-provider';
import { PageList } from '@/components/page-list';
import { StyledWrapper } from './all';
import { FavouritesIcon } from '@blocksuite/icons';
export const Favorite = () => {
const { pageList: allPageList } = useEditor();
return (
<>
<Header>
<StyledWrapper>
<FavouritesIcon />
Favorites
</StyledWrapper>
</Header>
<PageListHeader icon={<FavouritesIcon />}>Favorites</PageListHeader>
<PageList pageList={allPageList.filter(p => p.favorite && !p.trash)} />
</>
);
+2 -8
View File
@@ -1,18 +1,12 @@
import { Header } from '@/components/header';
import { PageListHeader } from '@/components/header';
import { useEditor } from '@/providers/editor-provider';
import { PageList } from '@/components/page-list';
import { StyledWrapper } from './all';
import { TrashIcon } from '@blocksuite/icons';
export const Trash = () => {
const { pageList: allPageList } = useEditor();
return (
<>
<Header>
<StyledWrapper>
<TrashIcon />
Trash
</StyledWrapper>
</Header>
<PageListHeader icon={<TrashIcon />}>Trash</PageListHeader>
<PageList pageList={allPageList.filter(p => p.trash)} isTrash={true} />
</>
);
@@ -42,7 +42,7 @@ export const EditorProvider = ({
const { mode, setMode } = useMode({ workspace, page });
const editorHandlers = useEditorHandler({ workspace, editor });
const editorHandlers = useEditorHandler({ workspace, editor, page });
const onPropsUpdated = usePropsUpdated(editor);
const onHistoryUpdated = useHistoryUpdated(page);
// Modify the updatedDate when history change
@@ -113,6 +113,11 @@ const EditorReactor = ({
editor.mode = pageMeta.mode;
}
if (pageMeta?.trash) {
// @ts-ignore
editor.readonly = true;
}
setEditor(editor);
}, [workspace, currentPage, setEditor]);
@@ -1,15 +1,17 @@
import { QueryContent } from '@blocksuite/store/dist/workspace/search';
import { createPage, initialPage, generateDefaultPageId } from '../utils';
import { Workspace } from '@blocksuite/store';
import { Workspace, Page } from '@blocksuite/store';
import { useRouter } from 'next/router';
import { EditorHandlers, PageMeta } from '../interface';
import { EditorContainer } from '@blocksuite/editor';
import toast from '@/components/toast';
export const useEditorHandler = ({
editor,
workspace,
page,
}: {
workspace?: Workspace;
page?: Page;
editor?: EditorContainer;
}): EditorHandlers => {
const router = useRouter();
@@ -20,8 +22,8 @@ export const useEditorHandler = ({
initialPage(page, title);
return page;
},
getPageMeta(pageId: string) {
pageId = pageId.replace('space:', '');
getPageMeta(pageId) {
pageId = (pageId ?? page!.id).replace('space:', '');
return workspace!.meta.pageMetas.find(
page => page.id === pageId
) as PageMeta;
@@ -42,22 +44,26 @@ export const useEditorHandler = ({
trash: !pageMeta.trash,
trashDate: +new Date(),
});
if (pageMeta.trash) {
toast('Removed to trash');
const editor = document.querySelector('editor-container');
// @ts-ignore
editor?.setAttribute('readonly', 'false');
}
}
},
favoritePage: pageId => {
workspace!.setPageMeta(pageId, { favorite: true });
},
permanentlyDeletePage: pageId => {
// TODO: workspace.meta.removePage or workspace.removePage?
workspace!.meta.removePage(pageId);
},
unFavoritePage: pageId => {
workspace!.setPageMeta(pageId, { favorite: true });
},
toggleFavoritePage: pageId => {
const pageMeta = workspace?.meta.pageMetas.find(p => p.id === pageId);
if (pageMeta) {
workspace!.setPageMeta(pageId, { favorite: !pageMeta.favorite });
toast(
pageMeta.favorite ? 'Removed to Favourites' : 'Added to Favourites'
);
}
},
search: (query: QueryContent) => {
@@ -31,10 +31,8 @@ export type EditorHandlers = {
pageId: string,
query?: { [key: string]: string }
) => Promise<boolean>;
getPageMeta: (pageId: string) => PageMeta | void;
getPageMeta: (pageId?: string) => PageMeta | void;
toggleDeletePage: (pageId: string) => void;
favoritePage: (pageId: string) => void;
unFavoritePage: (pageId: string) => void;
toggleFavoritePage: (pageId: string) => void;
permanentlyDeletePage: (pageId: string) => void;
search: (query: QueryContent) => Map<string, string | undefined>;
+4 -2
View File
@@ -16,6 +16,7 @@ export const getLightTheme = (
pageBackground: '#fff',
hoverBackground: '#F1F3FF',
innerHoverBackground: '#E0E6FF',
popoverBackground: '#fff',
tooltipBackground: '#6880FF',
codeBackground: '#f2f5f9',
@@ -24,7 +25,7 @@ export const getLightTheme = (
textColor: '#3A4C5C',
edgelessTextColor: '#3A4C5C',
iconColor: '#9096A5',
iconColor: '#888a9e',
linkColor: '#6880FF',
linkColor2: '#6880FF',
linkVisitedColor: '#ABB8FE',
@@ -87,6 +88,7 @@ export const getDarkTheme = (
pageBackground: '#2c2c2c',
hoverBackground: '#3C3C42',
innerHoverBackground: '#E0E6FF',
popoverBackground: '#1F2021',
tooltipBackground: '#1F2021',
codeBackground:
@@ -98,7 +100,7 @@ export const getDarkTheme = (
textColor: '#fff',
edgelessTextColor: '#3A4C5C',
iconColor: '#9096A5',
iconColor: '#888a9e',
linkColor: '#7D91FF',
linkColor2: '#6880FF',
linkVisitedColor: '#505FAB',
+1
View File
@@ -23,6 +23,7 @@ export interface AffineTheme {
popoverBackground: string;
tooltipBackground: string;
hoverBackground: string;
innerHoverBackground: string;
codeBackground: string;
warningBackground: string;
errorBackground: string;
+1 -3
View File
@@ -4,8 +4,7 @@ import { ModalWrapper } from '@/ui/modal';
export const StyledModalWrapper = styled(ModalWrapper)(({ theme }) => {
return {
width: '460px',
height: '240px',
padding: '0 60px',
padding: '46px 60px 32px',
};
});
@@ -14,7 +13,6 @@ export const StyledConfirmTitle = styled.div(({ theme }) => {
fontSize: theme.font.h6,
fontWeight: 600,
textAlign: 'center',
marginTop: '45px',
color: theme.colors.popoverColor,
};
});
+15 -15
View File
@@ -22,10 +22,10 @@ importers:
packages/app:
specifiers:
'@blocksuite/blocks': 0.3.0-alpha.8
'@blocksuite/editor': 0.3.0-alpha.8
'@blocksuite/blocks': 0.3.0-alpha.9
'@blocksuite/editor': 0.3.0-alpha.9
'@blocksuite/icons': ^2.0.0
'@blocksuite/store': 0.3.0-alpha.8
'@blocksuite/store': 0.3.0-alpha.9
'@emotion/css': ^11.10.0
'@emotion/react': ^11.10.4
'@emotion/server': ^11.10.0
@@ -55,10 +55,10 @@ importers:
react-dom: 18.2.0
typescript: 4.8.3
dependencies:
'@blocksuite/blocks': 0.3.0-alpha.8
'@blocksuite/editor': 0.3.0-alpha.8
'@blocksuite/blocks': 0.3.0-alpha.9
'@blocksuite/editor': 0.3.0-alpha.9
'@blocksuite/icons': 2.0.0_w5j4k42lgipnm43s3brx6h3c34
'@blocksuite/store': 0.3.0-alpha.8
'@blocksuite/store': 0.3.0-alpha.9
'@emotion/css': 11.10.0
'@emotion/react': 11.10.4_w5j4k42lgipnm43s3brx6h3c34
'@emotion/server': 11.10.0_@emotion+css@11.10.0
@@ -176,10 +176,10 @@ packages:
to-fast-properties: 2.0.0
dev: false
/@blocksuite/blocks/0.3.0-alpha.8:
resolution: {integrity: sha512-Q7md4N1H0lTMG0nX1PnAdSlTjBfNX4nFLlcX3SrdgjSM9QOu0G6E6OvfHHHCj4SJPnzyHH5xb6Lb0jU9Lpas6Q==}
/@blocksuite/blocks/0.3.0-alpha.9:
resolution: {integrity: sha512-7Zw2lL5DZ+JB6dtmGKfuW19nG5gMvLPcTakK9oMszbhgETnwHUUN382Pz3urecuDA88yaLj8qbC1VdbAQWDW5w==}
dependencies:
'@blocksuite/store': 0.3.0-alpha.8
'@blocksuite/store': 0.3.0-alpha.9
'@tldraw/intersect': 1.8.0
'@tldraw/vec': 1.8.0
hotkeys-js: 3.10.0
@@ -193,11 +193,11 @@ packages:
- utf-8-validate
dev: false
/@blocksuite/editor/0.3.0-alpha.8:
resolution: {integrity: sha512-Sx5E69LMZkUcc37q2PLXAnNTHy9okn/UFBfcWG8i5LO/T8QpYJgbllZGJDx7A3qP8Uz+BQyhgI5gp8S/dnra7g==}
/@blocksuite/editor/0.3.0-alpha.9:
resolution: {integrity: sha512-dnrk87akzwRRKxS18dec2rY2ZvfIpvNq9SIHEPCCk2UNLI2R66HUO5v1RhLi6/oglbWtizcqzQWCa/DjjY1ljA==}
dependencies:
'@blocksuite/blocks': 0.3.0-alpha.8
'@blocksuite/store': 0.3.0-alpha.8
'@blocksuite/blocks': 0.3.0-alpha.9
'@blocksuite/store': 0.3.0-alpha.9
lit: 2.4.0
marked: 4.1.1
turndown: 7.1.1
@@ -217,8 +217,8 @@ packages:
react: 18.2.0
dev: false
/@blocksuite/store/0.3.0-alpha.8:
resolution: {integrity: sha512-u63FWZSiYID8++tEIaQZ5FSj7NZu9XM2ZdTaTU+7PDEwucK5Gqo8+YrEC9gVl+2jkSW5cp43pxf2kZolUgkyYg==}
/@blocksuite/store/0.3.0-alpha.9:
resolution: {integrity: sha512-5wdnE6JUL1/y/LufqcRg2lRL5mYvdXc+MOj98eNfFAv+JFbY4vuGZ8N9HFn6dQ/rQaQsgaoAZkWyogbbC8x4IA==}
dependencies:
buffer: 6.0.3
flexsearch: 0.7.21