diff --git a/packages/app/package.json b/packages/app/package.json index a0176aecdd..46964ecf32 100644 --- a/packages/app/package.json +++ b/packages/app/package.json @@ -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", diff --git a/packages/app/src/components/editor-mode-switch/index.tsx b/packages/app/src/components/editor-mode-switch/index.tsx index 2d807ad857..e2c1838d8b 100644 --- a/packages/app/src/components/editor-mode-switch/index.tsx +++ b/packages/app/src/components/editor-mode-switch/index.tsx @@ -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} > ( - ({ 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; diff --git a/packages/app/src/components/faq/index.tsx b/packages/app/src/components/faq/index.tsx deleted file mode 100644 index 6c22caffb4..0000000000 --- a/packages/app/src/components/faq/index.tsx +++ /dev/null @@ -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 ( - <> - { - setShowContent(true); - }} - onMouseLeave={() => { - setShowContent(false); - }} - > - - - - { - setShowContent(false); - triggerContactModal(); - }} - > - - - - - { - setShowContent(false); - triggerShortcutsModal(); - }} - > - - - - - - -
- - - - - - -
-
- - ); -}; diff --git a/packages/app/src/components/header/page-header.tsx b/packages/app/src/components/header/editor-header.tsx similarity index 70% rename from packages/app/src/components/header/page-header.tsx rename to packages/app/src/components/header/editor-header.tsx index 1cccd703f0..6a6c409fe9 100644 --- a/packages/app/src/components/header/page-header.tsx +++ b/packages/app/src/components/header/editor-header.tsx @@ -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 (
{title && ( { + if (pageMeta?.trash) return; + setIsHover(true); }} onMouseLeave={() => { + if (pageMeta?.trash) return; + setIsHover(false); }} > @@ -53,13 +55,7 @@ export const PageHeader = () => { {title} - { - triggerQuickSearchModal(); - }} - > - - + @@ -68,4 +64,4 @@ export const PageHeader = () => { ); }; -export default PageHeader; +export default EditorHeader; diff --git a/packages/app/src/components/header/index.tsx b/packages/app/src/components/header/index.tsx index 3f74014b0c..d31b8f7abb 100644 --- a/packages/app/src/components/header/index.tsx +++ b/packages/app/src/components/header/index.tsx @@ -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'; diff --git a/packages/app/src/components/header/page-list-header.tsx b/packages/app/src/components/header/page-list-header.tsx new file mode 100644 index 0000000000..452061fbd4 --- /dev/null +++ b/packages/app/src/components/header/page-list-header.tsx @@ -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 ( +
+ + {icon} + {children} + + +
+ ); +}; + +export default PageListHeader; diff --git a/packages/app/src/components/header/quick-search-button.tsx b/packages/app/src/components/header/quick-search-button.tsx new file mode 100644 index 0000000000..3aadb9a6aa --- /dev/null +++ b/packages/app/src/components/header/quick-search-button.tsx @@ -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) => { + const { triggerQuickSearchModal } = useModal(); + + return ( + { + onClick?.(e); + triggerQuickSearchModal(); + }} + > + + + ); +}; + +export default QuickSearchButton; diff --git a/packages/app/src/components/header/styles.ts b/packages/app/src/components/header/styles.ts index d6baee2926..faa348266f 100644 --- a/packages/app/src/components/header/styles.ts +++ b/packages/app/src/components/header/styles.ts @@ -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', + }, + }; +}); diff --git a/packages/app/src/components/faq/icons.tsx b/packages/app/src/components/help-island/icons.tsx similarity index 100% rename from packages/app/src/components/faq/icons.tsx rename to packages/app/src/components/help-island/icons.tsx diff --git a/packages/app/src/components/help-island/index.tsx b/packages/app/src/components/help-island/index.tsx new file mode 100644 index 0000000000..b8e44102e2 --- /dev/null +++ b/packages/app/src/components/help-island/index.tsx @@ -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; + +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 ( + <> + { + setShowContent(true); + }} + onMouseLeave={() => { + setShowContent(false); + }} + > + + + {showList.includes('contact') && ( + + { + setShowContent(false); + triggerContactModal(); + }} + > + + + + )} + {showList.includes('shortcuts') && ( + + { + setShowContent(false); + triggerShortcutsModal(); + }} + > + + + + )} + + + +
+ + + + + + +
+
+ + ); +}; + +export default HelpIsland; diff --git a/packages/app/src/components/faq/style.ts b/packages/app/src/components/help-island/style.ts similarity index 93% rename from packages/app/src/components/faq/style.ts rename to packages/app/src/components/help-island/style.ts index e39e6280c5..c31179005e 100644 --- a/packages/app/src/components/faq/style.ts +++ b/packages/app/src/components/help-island/style.ts @@ -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%', diff --git a/packages/app/src/components/page-list/date-cell.tsx b/packages/app/src/components/page-list/date-cell.tsx index 249a07c99e..e7d90f64e8 100644 --- a/packages/app/src/components/page-list/date-cell.tsx +++ b/packages/app/src/components/page-list/date-cell.tsx @@ -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 ( - {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') : '--'} ); }; diff --git a/packages/app/src/components/page-list/index.tsx b/packages/app/src/components/page-list/index.tsx index 70b232d026..5fbba9d98e 100644 --- a/packages/app/src/components/page-list/index.tsx +++ b/packages/app/src/components/page-list/index.tsx @@ -85,6 +85,7 @@ export const PageList = ({ {isTrash ? ( diff --git a/packages/app/src/components/page-list/operation-cell.tsx b/packages/app/src/components/page-list/operation-cell.tsx index b487a68df4..4f306e6d83 100644 --- a/packages/app/src/components/page-list/operation-cell.tsx +++ b/packages/app/src/components/page-list/operation-cell.tsx @@ -40,9 +40,8 @@ export const OperationCell = ({ pageMeta }: { pageMeta: PageMeta }) => { { 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 => { diff --git a/packages/app/src/components/toast.ts b/packages/app/src/components/toast.ts new file mode 100644 index 0000000000..1ca6fdcfe7 --- /dev/null +++ b/packages/app/src/components/toast.ts @@ -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 = (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`
`; + const element = htmlToElement(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`
`; + const element = htmlToElement(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; diff --git a/packages/app/src/components/workspace-slider-bar/index.tsx b/packages/app/src/components/workspace-slider-bar/index.tsx index b47e7cbe9e..9c1616a63c 100644 --- a/packages/app/src/components/workspace-slider-bar/index.tsx +++ b/packages/app/src/components/workspace-slider-bar/index.tsx @@ -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'} ); })} @@ -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 ( <> @@ -120,14 +124,27 @@ export const WorkSpaceSliderBar = () => { New Page - { - setShow(!show); - }} + - - + { + setShow(!show); + setShowTip(false); + }} + onMouseEnter={() => { + setShowTip(true); + }} + onMouseLeave={() => { + setShowTip(false); + }} + > + + + ); }; diff --git a/packages/app/src/hooks/use-local-storage.ts b/packages/app/src/hooks/use-local-storage.ts new file mode 100644 index 0000000000..ee4e630c6a --- /dev/null +++ b/packages/app/src/hooks/use-local-storage.ts @@ -0,0 +1,33 @@ +import { Dispatch, SetStateAction, useEffect, useState } from 'react'; + +export type UseLocalStorage = ( + key: string, + initialState: S | (() => S), + // If initialState is different from the initial local data, set it + initialValue?: S +) => [S, Dispatch>]; + +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; diff --git a/packages/app/src/pages/_app.tsx b/packages/app/src/pages/_app.tsx index 5311547382..eda404eee2 100644 --- a/packages/app/src/pages/_app.tsx +++ b/packages/app/src/pages/_app.tsx @@ -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 ( <> @@ -50,6 +55,9 @@ function MyApp({ Component, pageProps }: AppProps) { + diff --git a/packages/app/src/pages/affine.tsx b/packages/app/src/pages/affine.tsx index a486a254d0..89609d3ada 100644 --- a/packages/app/src/pages/affine.tsx +++ b/packages/app/src/pages/affine.tsx @@ -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 = () => { - + diff --git a/packages/app/src/pages/index.tsx b/packages/app/src/pages/index.tsx index 8650026334..fcc7770e04 100644 --- a/packages/app/src/pages/index.tsx +++ b/packages/app/src/pages/index.tsx @@ -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 ( <> - + - ); diff --git a/packages/app/src/pages/page-list/all.tsx b/packages/app/src/pages/page-list/all.tsx index e3eb7f5d81..21c040a687 100644 --- a/packages/app/src/pages/page-list/all.tsx +++ b/packages/app/src/pages/page-list/all.tsx @@ -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 ( <> -
- - - All Pages - -
+ }>All Page !p.trash)} showFavoriteTag={true} diff --git a/packages/app/src/pages/page-list/favorite.tsx b/packages/app/src/pages/page-list/favorite.tsx index 7d4c181a69..400c7692da 100644 --- a/packages/app/src/pages/page-list/favorite.tsx +++ b/packages/app/src/pages/page-list/favorite.tsx @@ -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 ( <> -
- - - Favorites - -
+ }>Favorites p.favorite && !p.trash)} /> ); diff --git a/packages/app/src/pages/page-list/trash.tsx b/packages/app/src/pages/page-list/trash.tsx index 869908514b..be71dc9ecd 100644 --- a/packages/app/src/pages/page-list/trash.tsx +++ b/packages/app/src/pages/page-list/trash.tsx @@ -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 ( <> -
- - - Trash - -
+ }>Trash p.trash)} isTrash={true} /> ); diff --git a/packages/app/src/providers/editor-provider/editor-provider.tsx b/packages/app/src/providers/editor-provider/editor-provider.tsx index 9956cec2bf..9dc473966b 100644 --- a/packages/app/src/providers/editor-provider/editor-provider.tsx +++ b/packages/app/src/providers/editor-provider/editor-provider.tsx @@ -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 diff --git a/packages/app/src/providers/editor-provider/editor-reactor.tsx b/packages/app/src/providers/editor-provider/editor-reactor.tsx index 99387ab192..bbaacb876d 100644 --- a/packages/app/src/providers/editor-provider/editor-reactor.tsx +++ b/packages/app/src/providers/editor-provider/editor-reactor.tsx @@ -113,6 +113,11 @@ const EditorReactor = ({ editor.mode = pageMeta.mode; } + if (pageMeta?.trash) { + // @ts-ignore + editor.readonly = true; + } + setEditor(editor); }, [workspace, currentPage, setEditor]); diff --git a/packages/app/src/providers/editor-provider/hooks/useEditorHandler.ts b/packages/app/src/providers/editor-provider/hooks/useEditorHandler.ts index 64091eb0a1..651ec6f136 100644 --- a/packages/app/src/providers/editor-provider/hooks/useEditorHandler.ts +++ b/packages/app/src/providers/editor-provider/hooks/useEditorHandler.ts @@ -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) => { diff --git a/packages/app/src/providers/editor-provider/interface.ts b/packages/app/src/providers/editor-provider/interface.ts index 751a9bc360..afb96d1442 100644 --- a/packages/app/src/providers/editor-provider/interface.ts +++ b/packages/app/src/providers/editor-provider/interface.ts @@ -31,10 +31,8 @@ export type EditorHandlers = { pageId: string, query?: { [key: string]: string } ) => Promise; - 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; diff --git a/packages/app/src/styles/theme.ts b/packages/app/src/styles/theme.ts index 18ca3b7163..54ca29691a 100644 --- a/packages/app/src/styles/theme.ts +++ b/packages/app/src/styles/theme.ts @@ -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', diff --git a/packages/app/src/styles/types.ts b/packages/app/src/styles/types.ts index 0a69af4b47..b3c615009f 100644 --- a/packages/app/src/styles/types.ts +++ b/packages/app/src/styles/types.ts @@ -23,6 +23,7 @@ export interface AffineTheme { popoverBackground: string; tooltipBackground: string; hoverBackground: string; + innerHoverBackground: string; codeBackground: string; warningBackground: string; errorBackground: string; diff --git a/packages/app/src/ui/confirm/styles.ts b/packages/app/src/ui/confirm/styles.ts index 54639771e7..731b95f274 100644 --- a/packages/app/src/ui/confirm/styles.ts +++ b/packages/app/src/ui/confirm/styles.ts @@ -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, }; }); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index c1eed11335..055f4da9f8 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -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