mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-08-18 18:41:52 +08:00
Merge branch 'feat/fileSystem' into develop
This commit is contained in:
@@ -9,10 +9,10 @@
|
|||||||
"lint": "next lint"
|
"lint": "next lint"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@blocksuite/blocks": "0.3.0-alpha.8",
|
"@blocksuite/blocks": "0.3.0-alpha.9",
|
||||||
"@blocksuite/editor": "0.3.0-alpha.8",
|
"@blocksuite/editor": "0.3.0-alpha.9",
|
||||||
"@blocksuite/icons": "^2.0.0",
|
"@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/css": "^11.10.0",
|
||||||
"@emotion/react": "^11.10.4",
|
"@emotion/react": "^11.10.4",
|
||||||
"@emotion/server": "^11.10.0",
|
"@emotion/server": "^11.10.0",
|
||||||
|
|||||||
@@ -66,7 +66,9 @@ export const EditorModeSwitch = ({
|
|||||||
style = {},
|
style = {},
|
||||||
}: AnimateRadioProps) => {
|
}: AnimateRadioProps) => {
|
||||||
const { mode: themeMode } = useTheme();
|
const { mode: themeMode } = useTheme();
|
||||||
const { mode, setMode } = useEditor();
|
const { mode, setMode, getPageMeta } = useEditor();
|
||||||
|
const pageMeta = getPageMeta();
|
||||||
|
|
||||||
const modifyRadioItemStatus = (): RadioItemStatus => {
|
const modifyRadioItemStatus = (): RadioItemStatus => {
|
||||||
return {
|
return {
|
||||||
left: isHover
|
left: isHover
|
||||||
@@ -99,6 +101,7 @@ export const EditorModeSwitch = ({
|
|||||||
data-testid="editor-mode-switcher"
|
data-testid="editor-mode-switcher"
|
||||||
shrink={!isHover}
|
shrink={!isHover}
|
||||||
style={style}
|
style={style}
|
||||||
|
disabled={!!pageMeta?.trash}
|
||||||
>
|
>
|
||||||
<AnimateRadioItem
|
<AnimateRadioItem
|
||||||
isLeft={true}
|
isLeft={true}
|
||||||
|
|||||||
@@ -5,41 +5,42 @@ import type { ItemStatus } from './type';
|
|||||||
|
|
||||||
const ANIMATE_DURATION = 500;
|
const ANIMATE_DURATION = 500;
|
||||||
|
|
||||||
export const StyledAnimateRadioContainer = styled('div')<{ shrink: boolean }>(
|
export const StyledAnimateRadioContainer = styled('div')<{
|
||||||
({ shrink, theme }) => {
|
shrink: boolean;
|
||||||
const animateScaleStretch = keyframes`${toString(
|
disabled: boolean;
|
||||||
spring({ width: '36px' }, { width: '160px' }, { preset: 'gentle' })
|
}>(({ shrink, theme, disabled }) => {
|
||||||
)}`;
|
const animateScaleStretch = keyframes`${toString(
|
||||||
const animateScaleShrink = keyframes(
|
spring({ width: '36px' }, { width: '160px' }, { preset: 'gentle' })
|
||||||
`${toString(
|
)}`;
|
||||||
spring({ width: '160px' }, { width: '36px' }, { preset: 'gentle' })
|
const animateScaleShrink = keyframes(
|
||||||
)}`
|
`${toString(
|
||||||
);
|
spring({ width: '160px' }, { width: '36px' }, { preset: 'gentle' })
|
||||||
const shrinkStyle = shrink
|
)}`
|
||||||
? {
|
);
|
||||||
animation: `${animateScaleShrink} ${ANIMATE_DURATION}ms forwards`,
|
const shrinkStyle = shrink
|
||||||
background: 'transparent',
|
? {
|
||||||
}
|
animation: `${animateScaleShrink} ${ANIMATE_DURATION}ms forwards`,
|
||||||
: {
|
background: 'transparent',
|
||||||
animation: `${animateScaleStretch} ${ANIMATE_DURATION}ms forwards`,
|
}
|
||||||
};
|
: {
|
||||||
|
animation: `${animateScaleStretch} ${ANIMATE_DURATION}ms forwards`,
|
||||||
|
};
|
||||||
|
|
||||||
return {
|
return {
|
||||||
height: '36px',
|
height: '36px',
|
||||||
borderRadius: '18px',
|
borderRadius: '18px',
|
||||||
background: theme.colors.hoverBackground,
|
background: disabled ? 'transparent' : theme.colors.hoverBackground,
|
||||||
position: 'relative',
|
position: 'relative',
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
transition: `background ${ANIMATE_DURATION}ms, border ${ANIMATE_DURATION}ms`,
|
transition: `background ${ANIMATE_DURATION}ms, border ${ANIMATE_DURATION}ms`,
|
||||||
border: '1px solid transparent',
|
border: '1px solid transparent',
|
||||||
|
|
||||||
...shrinkStyle,
|
...(disabled ? { pointerEvents: 'none' } : shrinkStyle),
|
||||||
':hover': {
|
':hover': {
|
||||||
border: `1px solid ${theme.colors.primaryColor}`,
|
border: disabled ? '' : `1px solid ${theme.colors.primaryColor}`,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
});
|
||||||
);
|
|
||||||
|
|
||||||
export const StyledMiddleLine = styled('div')<{
|
export const StyledMiddleLine = styled('div')<{
|
||||||
hidden: boolean;
|
hidden: boolean;
|
||||||
|
|||||||
@@ -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>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
+12
-16
@@ -5,25 +5,21 @@ import {
|
|||||||
StyledTitle,
|
StyledTitle,
|
||||||
StyledTitleWrapper,
|
StyledTitleWrapper,
|
||||||
} from './styles';
|
} from './styles';
|
||||||
import { IconButton } from '@/ui/button';
|
|
||||||
import { Content } from '@/ui/layout';
|
import { Content } from '@/ui/layout';
|
||||||
import { useEditor } from '@/providers/editor-provider';
|
import { useEditor } from '@/providers/editor-provider';
|
||||||
import EditorModeSwitch from '@/components/editor-mode-switch';
|
import EditorModeSwitch from '@/components/editor-mode-switch';
|
||||||
import { ArrowDownIcon } from '@blocksuite/icons';
|
import QuickSearchButton from './quick-search-button';
|
||||||
import { useModal } from '@/providers/global-modal-provider';
|
|
||||||
|
|
||||||
import Header from './header';
|
import Header from './header';
|
||||||
|
|
||||||
export const PageHeader = () => {
|
export const EditorHeader = () => {
|
||||||
const [title, setTitle] = useState('');
|
const [title, setTitle] = useState('');
|
||||||
const [isHover, setIsHover] = useState(false);
|
const [isHover, setIsHover] = useState(false);
|
||||||
|
|
||||||
const { editor, onPropsUpdated } = useEditor();
|
const { editor, onPropsUpdated, getPageMeta } = useEditor();
|
||||||
const { triggerQuickSearchModal } = useModal();
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
onPropsUpdated(editor => {
|
onPropsUpdated(editor => {
|
||||||
setTitle(editor.model.title);
|
setTitle(editor.model.title || 'Untitled');
|
||||||
});
|
});
|
||||||
}, [onPropsUpdated]);
|
}, [onPropsUpdated]);
|
||||||
|
|
||||||
@@ -31,14 +27,20 @@ export const PageHeader = () => {
|
|||||||
setTitle(editor?.model.title || 'Untitled');
|
setTitle(editor?.model.title || 'Untitled');
|
||||||
}, [editor]);
|
}, [editor]);
|
||||||
|
|
||||||
|
const pageMeta = getPageMeta();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Header>
|
<Header>
|
||||||
{title && (
|
{title && (
|
||||||
<StyledTitle
|
<StyledTitle
|
||||||
onMouseEnter={() => {
|
onMouseEnter={() => {
|
||||||
|
if (pageMeta?.trash) return;
|
||||||
|
|
||||||
setIsHover(true);
|
setIsHover(true);
|
||||||
}}
|
}}
|
||||||
onMouseLeave={() => {
|
onMouseLeave={() => {
|
||||||
|
if (pageMeta?.trash) return;
|
||||||
|
|
||||||
setIsHover(false);
|
setIsHover(false);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
@@ -53,13 +55,7 @@ export const PageHeader = () => {
|
|||||||
</StyledSwitchWrapper>
|
</StyledSwitchWrapper>
|
||||||
<Content ellipsis={true}>{title}</Content>
|
<Content ellipsis={true}>{title}</Content>
|
||||||
<StyledSearchArrowWrapper>
|
<StyledSearchArrowWrapper>
|
||||||
<IconButton
|
<QuickSearchButton />
|
||||||
onClick={() => {
|
|
||||||
triggerQuickSearchModal();
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<ArrowDownIcon />
|
|
||||||
</IconButton>
|
|
||||||
</StyledSearchArrowWrapper>
|
</StyledSearchArrowWrapper>
|
||||||
</StyledTitleWrapper>
|
</StyledTitleWrapper>
|
||||||
</StyledTitle>
|
</StyledTitle>
|
||||||
@@ -68,4 +64,4 @@ export const PageHeader = () => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default PageHeader;
|
export default EditorHeader;
|
||||||
@@ -1,3 +1,3 @@
|
|||||||
export * from './header';
|
export * from './header';
|
||||||
export * from './page-header';
|
export * from './editor-header';
|
||||||
export { StyledTitle as HeaderWrapper } from './styles';
|
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'),
|
...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;
|
||||||
+2
-2
@@ -1,6 +1,6 @@
|
|||||||
import { displayFlex, styled } from '@/styles';
|
import { displayFlex, styled } from '@/styles';
|
||||||
|
|
||||||
export const StyledFAQ = styled('div')(({ theme }) => {
|
export const StyledIsland = styled('div')(({ theme }) => {
|
||||||
return {
|
return {
|
||||||
width: '32px',
|
width: '32px',
|
||||||
height: '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 {
|
return {
|
||||||
position: 'absolute',
|
position: 'absolute',
|
||||||
bottom: '100%',
|
bottom: '100%',
|
||||||
@@ -9,16 +9,17 @@ dayjs.extend(localizedFormat);
|
|||||||
export const DateCell = ({
|
export const DateCell = ({
|
||||||
pageMeta,
|
pageMeta,
|
||||||
dateKey,
|
dateKey,
|
||||||
|
backupKey = '',
|
||||||
}: {
|
}: {
|
||||||
pageMeta: PageMeta;
|
pageMeta: PageMeta;
|
||||||
dateKey: keyof PageMeta;
|
dateKey: keyof PageMeta;
|
||||||
|
backupKey?: keyof PageMeta;
|
||||||
}) => {
|
}) => {
|
||||||
// dayjs().format('L LT');
|
// dayjs().format('L LT');
|
||||||
|
const value = pageMeta[dateKey] ?? pageMeta[backupKey];
|
||||||
return (
|
return (
|
||||||
<TableCell ellipsis={true}>
|
<TableCell ellipsis={true}>
|
||||||
{pageMeta[dateKey] === undefined
|
{value ? dayjs(value as string).format('YYYY-MM-DD HH:mm') : '--'}
|
||||||
? '--'
|
|
||||||
: dayjs(pageMeta[dateKey] as string).format('YYYY-MM-DD HH:mm')}
|
|
||||||
</TableCell>
|
</TableCell>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -85,6 +85,7 @@ export const PageList = ({
|
|||||||
<DateCell
|
<DateCell
|
||||||
pageMeta={pageMeta}
|
pageMeta={pageMeta}
|
||||||
dateKey={isTrash ? 'trashDate' : 'updatedDate'}
|
dateKey={isTrash ? 'trashDate' : 'updatedDate'}
|
||||||
|
backupKey={isTrash ? 'trashDate' : 'createDate'}
|
||||||
/>
|
/>
|
||||||
<TableCell style={{ padding: 0 }}>
|
<TableCell style={{ padding: 0 }}>
|
||||||
{isTrash ? (
|
{isTrash ? (
|
||||||
|
|||||||
@@ -40,9 +40,8 @@ export const OperationCell = ({ pageMeta }: { pageMeta: PageMeta }) => {
|
|||||||
<MenuItem
|
<MenuItem
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
confirm({
|
confirm({
|
||||||
title: 'Delete',
|
title: 'Delete page?',
|
||||||
content:
|
content: `${pageMeta.title} will be moved to Trash`,
|
||||||
'Deleted items will be moved to Trash Bin. Do you confirm?',
|
|
||||||
confirmText: 'Delete',
|
confirmText: 'Delete',
|
||||||
confirmType: 'danger',
|
confirmType: 'danger',
|
||||||
}).then(confirm => {
|
}).then(confirm => {
|
||||||
|
|||||||
@@ -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,
|
AddIcon,
|
||||||
} from '@blocksuite/icons';
|
} from '@blocksuite/icons';
|
||||||
import Link from 'next/link';
|
import Link from 'next/link';
|
||||||
|
import { Tooltip } from '@/ui/tooltip';
|
||||||
import { useEditor } from '@/providers/editor-provider';
|
import { useEditor } from '@/providers/editor-provider';
|
||||||
import { useModal } from '@/providers/global-modal-provider';
|
import { useModal } from '@/providers/global-modal-provider';
|
||||||
|
|
||||||
import { IconButton } from '@/ui/button';
|
import { IconButton } from '@/ui/button';
|
||||||
|
import useLocalStorage from '@/hooks/use-local-storage';
|
||||||
|
|
||||||
const FavoriteList = ({ showList }: { showList: boolean }) => {
|
const FavoriteList = ({ showList }: { showList: boolean }) => {
|
||||||
const { pageList, openPage } = useEditor();
|
const { pageList, openPage } = useEditor();
|
||||||
@@ -45,7 +47,7 @@ const FavoriteList = ({ showList }: { showList: boolean }) => {
|
|||||||
openPage(pageMeta.id);
|
openPage(pageMeta.id);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{pageMeta.title || pageMeta.id}
|
{pageMeta.title || 'Untitled'}
|
||||||
</StyledSubListItem>
|
</StyledSubListItem>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
@@ -57,11 +59,13 @@ const FavoriteList = ({ showList }: { showList: boolean }) => {
|
|||||||
};
|
};
|
||||||
export const WorkSpaceSliderBar = () => {
|
export const WorkSpaceSliderBar = () => {
|
||||||
const { triggerQuickSearchModal, triggerImportModal } = useModal();
|
const { triggerQuickSearchModal, triggerImportModal } = useModal();
|
||||||
const [show, setShow] = useState(false);
|
|
||||||
const [showSubFavorite, setShowSubFavorite] = useState(true);
|
const [showSubFavorite, setShowSubFavorite] = useState(true);
|
||||||
const { createPage, getPageMeta, openPage } = useEditor();
|
const { createPage, getPageMeta, openPage } = useEditor();
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
|
||||||
|
const [showTip, setShowTip] = useState(false);
|
||||||
|
const [show, setShow] = useLocalStorage('AFFINE_SLIDE_BAR', false, true);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<StyledSliderBar show={show}>
|
<StyledSliderBar show={show}>
|
||||||
@@ -120,14 +124,27 @@ export const WorkSpaceSliderBar = () => {
|
|||||||
<AddIcon /> New Page
|
<AddIcon /> New Page
|
||||||
</StyledNewPageButton>
|
</StyledNewPageButton>
|
||||||
</StyledSliderBar>
|
</StyledSliderBar>
|
||||||
<StyledArrowButton
|
<Tooltip
|
||||||
isShow={show}
|
content={show ? 'Collapse sidebar' : 'Expand sidebar'}
|
||||||
onClick={() => {
|
placement="right"
|
||||||
setShow(!show);
|
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;
|
||||||
@@ -13,6 +13,9 @@ import '../utils/print-build-info';
|
|||||||
import { styled } from '@/styles';
|
import { styled } from '@/styles';
|
||||||
import ProviderComposer from '@/components/provider-composer';
|
import ProviderComposer from '@/components/provider-composer';
|
||||||
import ConfirmProvider from '@/providers/confirm-provider';
|
import ConfirmProvider from '@/providers/confirm-provider';
|
||||||
|
import HelpIsland from '@/components/help-island';
|
||||||
|
import { useRouter } from 'next/router';
|
||||||
|
|
||||||
const ThemeProvider = dynamic(() => import('@/providers/themeProvider'), {
|
const ThemeProvider = dynamic(() => import('@/providers/themeProvider'), {
|
||||||
ssr: false,
|
ssr: false,
|
||||||
});
|
});
|
||||||
@@ -35,6 +38,8 @@ const StyledWrapper = styled('div')(({ theme }) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
function MyApp({ Component, pageProps }: AppProps) {
|
function MyApp({ Component, pageProps }: AppProps) {
|
||||||
|
const router = useRouter();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Logger />
|
<Logger />
|
||||||
@@ -50,6 +55,9 @@ function MyApp({ Component, pageProps }: AppProps) {
|
|||||||
<WorkSpaceSliderBar />
|
<WorkSpaceSliderBar />
|
||||||
<StyledWrapper>
|
<StyledWrapper>
|
||||||
<Component {...pageProps} />
|
<Component {...pageProps} />
|
||||||
|
<HelpIsland
|
||||||
|
showList={router.pathname !== '/' ? ['contact'] : undefined}
|
||||||
|
/>
|
||||||
</StyledWrapper>
|
</StyledWrapper>
|
||||||
</StyledPage>
|
</StyledPage>
|
||||||
</ProviderComposer>
|
</ProviderComposer>
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import Modal from '@/ui/modal';
|
|||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
import { Button } from '@/ui/button';
|
import { Button } from '@/ui/button';
|
||||||
import { FavouritedIcon } from '@blocksuite/icons';
|
import { FavouritedIcon } from '@blocksuite/icons';
|
||||||
|
import { toast } from '@/components/toast';
|
||||||
export const StyledHeader = styled('div')({
|
export const StyledHeader = styled('div')({
|
||||||
height: '60px',
|
height: '60px',
|
||||||
width: '100vw',
|
width: '100vw',
|
||||||
@@ -38,7 +39,14 @@ const Affine = () => {
|
|||||||
</Modal>
|
</Modal>
|
||||||
<Loading />
|
<Loading />
|
||||||
|
|
||||||
<Button icon={<FavouritedIcon />}>click me!</Button>
|
<Button
|
||||||
|
icon={<FavouritedIcon />}
|
||||||
|
onClick={() => {
|
||||||
|
toast('hello, world!!');
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
click me!
|
||||||
|
</Button>
|
||||||
<Button icon={<FavouritedIcon />} type={'primary'}>
|
<Button icon={<FavouritedIcon />} type={'primary'}>
|
||||||
click me!
|
click me!
|
||||||
</Button>
|
</Button>
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import type { NextPage } from 'next';
|
import type { NextPage } from 'next';
|
||||||
import { styled } from '@/styles';
|
import { styled } from '@/styles';
|
||||||
import { PageHeader } from '@/components/header';
|
import { EditorHeader } from '@/components/header';
|
||||||
import { FAQ } from '@/components/faq';
|
|
||||||
import EdgelessToolbar from '@/components/edgeless-toolbar';
|
import EdgelessToolbar from '@/components/edgeless-toolbar';
|
||||||
import MobileModal from '@/components/mobile-modal';
|
import MobileModal from '@/components/mobile-modal';
|
||||||
import Editor from '@/components/editor';
|
import Editor from '@/components/editor';
|
||||||
@@ -15,12 +14,11 @@ const StyledEditorContainer = styled('div')(({ theme }) => {
|
|||||||
const Home: NextPage = () => {
|
const Home: NextPage = () => {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<PageHeader />
|
<EditorHeader />
|
||||||
<MobileModal />
|
<MobileModal />
|
||||||
<StyledEditorContainer>
|
<StyledEditorContainer>
|
||||||
<Editor />
|
<Editor />
|
||||||
</StyledEditorContainer>
|
</StyledEditorContainer>
|
||||||
<FAQ />
|
|
||||||
<EdgelessToolbar />
|
<EdgelessToolbar />
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,32 +1,14 @@
|
|||||||
import { Header } from '@/components/header';
|
|
||||||
import { useEditor } from '@/providers/editor-provider';
|
import { useEditor } from '@/providers/editor-provider';
|
||||||
import { PageList } from '@/components/page-list';
|
import { PageList } from '@/components/page-list';
|
||||||
import { AllPagesIcon } from '@blocksuite/icons';
|
import { AllPagesIcon } from '@blocksuite/icons';
|
||||||
|
|
||||||
import { HeaderWrapper } from '@/components/header';
|
import { PageListHeader } 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',
|
|
||||||
},
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
export const All = () => {
|
export const All = () => {
|
||||||
const { pageList: allPageList } = useEditor();
|
const { pageList: allPageList } = useEditor();
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Header>
|
<PageListHeader icon={<AllPagesIcon />}>All Page</PageListHeader>
|
||||||
<StyledWrapper>
|
|
||||||
<AllPagesIcon />
|
|
||||||
All Pages
|
|
||||||
</StyledWrapper>
|
|
||||||
</Header>
|
|
||||||
<PageList
|
<PageList
|
||||||
pageList={allPageList.filter(p => !p.trash)}
|
pageList={allPageList.filter(p => !p.trash)}
|
||||||
showFavoriteTag={true}
|
showFavoriteTag={true}
|
||||||
|
|||||||
@@ -1,19 +1,13 @@
|
|||||||
import { Header } from '@/components/header';
|
import { PageListHeader } from '@/components/header';
|
||||||
import { useEditor } from '@/providers/editor-provider';
|
import { useEditor } from '@/providers/editor-provider';
|
||||||
import { PageList } from '@/components/page-list';
|
import { PageList } from '@/components/page-list';
|
||||||
import { StyledWrapper } from './all';
|
|
||||||
import { FavouritesIcon } from '@blocksuite/icons';
|
import { FavouritesIcon } from '@blocksuite/icons';
|
||||||
|
|
||||||
export const Favorite = () => {
|
export const Favorite = () => {
|
||||||
const { pageList: allPageList } = useEditor();
|
const { pageList: allPageList } = useEditor();
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Header>
|
<PageListHeader icon={<FavouritesIcon />}>Favorites</PageListHeader>
|
||||||
<StyledWrapper>
|
|
||||||
<FavouritesIcon />
|
|
||||||
Favorites
|
|
||||||
</StyledWrapper>
|
|
||||||
</Header>
|
|
||||||
<PageList pageList={allPageList.filter(p => p.favorite && !p.trash)} />
|
<PageList pageList={allPageList.filter(p => p.favorite && !p.trash)} />
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,18 +1,12 @@
|
|||||||
import { Header } from '@/components/header';
|
import { PageListHeader } from '@/components/header';
|
||||||
import { useEditor } from '@/providers/editor-provider';
|
import { useEditor } from '@/providers/editor-provider';
|
||||||
import { PageList } from '@/components/page-list';
|
import { PageList } from '@/components/page-list';
|
||||||
import { StyledWrapper } from './all';
|
|
||||||
import { TrashIcon } from '@blocksuite/icons';
|
import { TrashIcon } from '@blocksuite/icons';
|
||||||
export const Trash = () => {
|
export const Trash = () => {
|
||||||
const { pageList: allPageList } = useEditor();
|
const { pageList: allPageList } = useEditor();
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Header>
|
<PageListHeader icon={<TrashIcon />}>Trash</PageListHeader>
|
||||||
<StyledWrapper>
|
|
||||||
<TrashIcon />
|
|
||||||
Trash
|
|
||||||
</StyledWrapper>
|
|
||||||
</Header>
|
|
||||||
<PageList pageList={allPageList.filter(p => p.trash)} isTrash={true} />
|
<PageList pageList={allPageList.filter(p => p.trash)} isTrash={true} />
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ export const EditorProvider = ({
|
|||||||
|
|
||||||
const { mode, setMode } = useMode({ workspace, page });
|
const { mode, setMode } = useMode({ workspace, page });
|
||||||
|
|
||||||
const editorHandlers = useEditorHandler({ workspace, editor });
|
const editorHandlers = useEditorHandler({ workspace, editor, page });
|
||||||
const onPropsUpdated = usePropsUpdated(editor);
|
const onPropsUpdated = usePropsUpdated(editor);
|
||||||
const onHistoryUpdated = useHistoryUpdated(page);
|
const onHistoryUpdated = useHistoryUpdated(page);
|
||||||
// Modify the updatedDate when history change
|
// Modify the updatedDate when history change
|
||||||
|
|||||||
@@ -113,6 +113,11 @@ const EditorReactor = ({
|
|||||||
editor.mode = pageMeta.mode;
|
editor.mode = pageMeta.mode;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (pageMeta?.trash) {
|
||||||
|
// @ts-ignore
|
||||||
|
editor.readonly = true;
|
||||||
|
}
|
||||||
|
|
||||||
setEditor(editor);
|
setEditor(editor);
|
||||||
}, [workspace, currentPage, setEditor]);
|
}, [workspace, currentPage, setEditor]);
|
||||||
|
|
||||||
|
|||||||
@@ -1,15 +1,17 @@
|
|||||||
import { QueryContent } from '@blocksuite/store/dist/workspace/search';
|
import { QueryContent } from '@blocksuite/store/dist/workspace/search';
|
||||||
import { createPage, initialPage, generateDefaultPageId } from '../utils';
|
import { createPage, initialPage, generateDefaultPageId } from '../utils';
|
||||||
import { Workspace } from '@blocksuite/store';
|
import { Workspace, Page } from '@blocksuite/store';
|
||||||
import { useRouter } from 'next/router';
|
import { useRouter } from 'next/router';
|
||||||
import { EditorHandlers, PageMeta } from '../interface';
|
import { EditorHandlers, PageMeta } from '../interface';
|
||||||
import { EditorContainer } from '@blocksuite/editor';
|
import { EditorContainer } from '@blocksuite/editor';
|
||||||
|
import toast from '@/components/toast';
|
||||||
export const useEditorHandler = ({
|
export const useEditorHandler = ({
|
||||||
editor,
|
editor,
|
||||||
workspace,
|
workspace,
|
||||||
|
page,
|
||||||
}: {
|
}: {
|
||||||
workspace?: Workspace;
|
workspace?: Workspace;
|
||||||
|
page?: Page;
|
||||||
editor?: EditorContainer;
|
editor?: EditorContainer;
|
||||||
}): EditorHandlers => {
|
}): EditorHandlers => {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
@@ -20,8 +22,8 @@ export const useEditorHandler = ({
|
|||||||
initialPage(page, title);
|
initialPage(page, title);
|
||||||
return page;
|
return page;
|
||||||
},
|
},
|
||||||
getPageMeta(pageId: string) {
|
getPageMeta(pageId) {
|
||||||
pageId = pageId.replace('space:', '');
|
pageId = (pageId ?? page!.id).replace('space:', '');
|
||||||
return workspace!.meta.pageMetas.find(
|
return workspace!.meta.pageMetas.find(
|
||||||
page => page.id === pageId
|
page => page.id === pageId
|
||||||
) as PageMeta;
|
) as PageMeta;
|
||||||
@@ -42,22 +44,26 @@ export const useEditorHandler = ({
|
|||||||
trash: !pageMeta.trash,
|
trash: !pageMeta.trash,
|
||||||
trashDate: +new Date(),
|
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 => {
|
permanentlyDeletePage: pageId => {
|
||||||
// TODO: workspace.meta.removePage or workspace.removePage?
|
// TODO: workspace.meta.removePage or workspace.removePage?
|
||||||
workspace!.meta.removePage(pageId);
|
workspace!.meta.removePage(pageId);
|
||||||
},
|
},
|
||||||
unFavoritePage: pageId => {
|
|
||||||
workspace!.setPageMeta(pageId, { favorite: true });
|
|
||||||
},
|
|
||||||
toggleFavoritePage: pageId => {
|
toggleFavoritePage: pageId => {
|
||||||
const pageMeta = workspace?.meta.pageMetas.find(p => p.id === pageId);
|
const pageMeta = workspace?.meta.pageMetas.find(p => p.id === pageId);
|
||||||
if (pageMeta) {
|
if (pageMeta) {
|
||||||
workspace!.setPageMeta(pageId, { favorite: !pageMeta.favorite });
|
workspace!.setPageMeta(pageId, { favorite: !pageMeta.favorite });
|
||||||
|
toast(
|
||||||
|
pageMeta.favorite ? 'Removed to Favourites' : 'Added to Favourites'
|
||||||
|
);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
search: (query: QueryContent) => {
|
search: (query: QueryContent) => {
|
||||||
|
|||||||
@@ -31,10 +31,8 @@ export type EditorHandlers = {
|
|||||||
pageId: string,
|
pageId: string,
|
||||||
query?: { [key: string]: string }
|
query?: { [key: string]: string }
|
||||||
) => Promise<boolean>;
|
) => Promise<boolean>;
|
||||||
getPageMeta: (pageId: string) => PageMeta | void;
|
getPageMeta: (pageId?: string) => PageMeta | void;
|
||||||
toggleDeletePage: (pageId: string) => void;
|
toggleDeletePage: (pageId: string) => void;
|
||||||
favoritePage: (pageId: string) => void;
|
|
||||||
unFavoritePage: (pageId: string) => void;
|
|
||||||
toggleFavoritePage: (pageId: string) => void;
|
toggleFavoritePage: (pageId: string) => void;
|
||||||
permanentlyDeletePage: (pageId: string) => void;
|
permanentlyDeletePage: (pageId: string) => void;
|
||||||
search: (query: QueryContent) => Map<string, string | undefined>;
|
search: (query: QueryContent) => Map<string, string | undefined>;
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ export const getLightTheme = (
|
|||||||
|
|
||||||
pageBackground: '#fff',
|
pageBackground: '#fff',
|
||||||
hoverBackground: '#F1F3FF',
|
hoverBackground: '#F1F3FF',
|
||||||
|
innerHoverBackground: '#E0E6FF',
|
||||||
popoverBackground: '#fff',
|
popoverBackground: '#fff',
|
||||||
tooltipBackground: '#6880FF',
|
tooltipBackground: '#6880FF',
|
||||||
codeBackground: '#f2f5f9',
|
codeBackground: '#f2f5f9',
|
||||||
@@ -24,7 +25,7 @@ export const getLightTheme = (
|
|||||||
|
|
||||||
textColor: '#3A4C5C',
|
textColor: '#3A4C5C',
|
||||||
edgelessTextColor: '#3A4C5C',
|
edgelessTextColor: '#3A4C5C',
|
||||||
iconColor: '#9096A5',
|
iconColor: '#888a9e',
|
||||||
linkColor: '#6880FF',
|
linkColor: '#6880FF',
|
||||||
linkColor2: '#6880FF',
|
linkColor2: '#6880FF',
|
||||||
linkVisitedColor: '#ABB8FE',
|
linkVisitedColor: '#ABB8FE',
|
||||||
@@ -87,6 +88,7 @@ export const getDarkTheme = (
|
|||||||
|
|
||||||
pageBackground: '#2c2c2c',
|
pageBackground: '#2c2c2c',
|
||||||
hoverBackground: '#3C3C42',
|
hoverBackground: '#3C3C42',
|
||||||
|
innerHoverBackground: '#E0E6FF',
|
||||||
popoverBackground: '#1F2021',
|
popoverBackground: '#1F2021',
|
||||||
tooltipBackground: '#1F2021',
|
tooltipBackground: '#1F2021',
|
||||||
codeBackground:
|
codeBackground:
|
||||||
@@ -98,7 +100,7 @@ export const getDarkTheme = (
|
|||||||
|
|
||||||
textColor: '#fff',
|
textColor: '#fff',
|
||||||
edgelessTextColor: '#3A4C5C',
|
edgelessTextColor: '#3A4C5C',
|
||||||
iconColor: '#9096A5',
|
iconColor: '#888a9e',
|
||||||
linkColor: '#7D91FF',
|
linkColor: '#7D91FF',
|
||||||
linkColor2: '#6880FF',
|
linkColor2: '#6880FF',
|
||||||
linkVisitedColor: '#505FAB',
|
linkVisitedColor: '#505FAB',
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ export interface AffineTheme {
|
|||||||
popoverBackground: string;
|
popoverBackground: string;
|
||||||
tooltipBackground: string;
|
tooltipBackground: string;
|
||||||
hoverBackground: string;
|
hoverBackground: string;
|
||||||
|
innerHoverBackground: string;
|
||||||
codeBackground: string;
|
codeBackground: string;
|
||||||
warningBackground: string;
|
warningBackground: string;
|
||||||
errorBackground: string;
|
errorBackground: string;
|
||||||
|
|||||||
@@ -4,8 +4,7 @@ import { ModalWrapper } from '@/ui/modal';
|
|||||||
export const StyledModalWrapper = styled(ModalWrapper)(({ theme }) => {
|
export const StyledModalWrapper = styled(ModalWrapper)(({ theme }) => {
|
||||||
return {
|
return {
|
||||||
width: '460px',
|
width: '460px',
|
||||||
height: '240px',
|
padding: '46px 60px 32px',
|
||||||
padding: '0 60px',
|
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -14,7 +13,6 @@ export const StyledConfirmTitle = styled.div(({ theme }) => {
|
|||||||
fontSize: theme.font.h6,
|
fontSize: theme.font.h6,
|
||||||
fontWeight: 600,
|
fontWeight: 600,
|
||||||
textAlign: 'center',
|
textAlign: 'center',
|
||||||
marginTop: '45px',
|
|
||||||
color: theme.colors.popoverColor,
|
color: theme.colors.popoverColor,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|||||||
Generated
+15
-15
@@ -22,10 +22,10 @@ importers:
|
|||||||
|
|
||||||
packages/app:
|
packages/app:
|
||||||
specifiers:
|
specifiers:
|
||||||
'@blocksuite/blocks': 0.3.0-alpha.8
|
'@blocksuite/blocks': 0.3.0-alpha.9
|
||||||
'@blocksuite/editor': 0.3.0-alpha.8
|
'@blocksuite/editor': 0.3.0-alpha.9
|
||||||
'@blocksuite/icons': ^2.0.0
|
'@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/css': ^11.10.0
|
||||||
'@emotion/react': ^11.10.4
|
'@emotion/react': ^11.10.4
|
||||||
'@emotion/server': ^11.10.0
|
'@emotion/server': ^11.10.0
|
||||||
@@ -55,10 +55,10 @@ importers:
|
|||||||
react-dom: 18.2.0
|
react-dom: 18.2.0
|
||||||
typescript: 4.8.3
|
typescript: 4.8.3
|
||||||
dependencies:
|
dependencies:
|
||||||
'@blocksuite/blocks': 0.3.0-alpha.8
|
'@blocksuite/blocks': 0.3.0-alpha.9
|
||||||
'@blocksuite/editor': 0.3.0-alpha.8
|
'@blocksuite/editor': 0.3.0-alpha.9
|
||||||
'@blocksuite/icons': 2.0.0_w5j4k42lgipnm43s3brx6h3c34
|
'@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/css': 11.10.0
|
||||||
'@emotion/react': 11.10.4_w5j4k42lgipnm43s3brx6h3c34
|
'@emotion/react': 11.10.4_w5j4k42lgipnm43s3brx6h3c34
|
||||||
'@emotion/server': 11.10.0_@emotion+css@11.10.0
|
'@emotion/server': 11.10.0_@emotion+css@11.10.0
|
||||||
@@ -176,10 +176,10 @@ packages:
|
|||||||
to-fast-properties: 2.0.0
|
to-fast-properties: 2.0.0
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/@blocksuite/blocks/0.3.0-alpha.8:
|
/@blocksuite/blocks/0.3.0-alpha.9:
|
||||||
resolution: {integrity: sha512-Q7md4N1H0lTMG0nX1PnAdSlTjBfNX4nFLlcX3SrdgjSM9QOu0G6E6OvfHHHCj4SJPnzyHH5xb6Lb0jU9Lpas6Q==}
|
resolution: {integrity: sha512-7Zw2lL5DZ+JB6dtmGKfuW19nG5gMvLPcTakK9oMszbhgETnwHUUN382Pz3urecuDA88yaLj8qbC1VdbAQWDW5w==}
|
||||||
dependencies:
|
dependencies:
|
||||||
'@blocksuite/store': 0.3.0-alpha.8
|
'@blocksuite/store': 0.3.0-alpha.9
|
||||||
'@tldraw/intersect': 1.8.0
|
'@tldraw/intersect': 1.8.0
|
||||||
'@tldraw/vec': 1.8.0
|
'@tldraw/vec': 1.8.0
|
||||||
hotkeys-js: 3.10.0
|
hotkeys-js: 3.10.0
|
||||||
@@ -193,11 +193,11 @@ packages:
|
|||||||
- utf-8-validate
|
- utf-8-validate
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/@blocksuite/editor/0.3.0-alpha.8:
|
/@blocksuite/editor/0.3.0-alpha.9:
|
||||||
resolution: {integrity: sha512-Sx5E69LMZkUcc37q2PLXAnNTHy9okn/UFBfcWG8i5LO/T8QpYJgbllZGJDx7A3qP8Uz+BQyhgI5gp8S/dnra7g==}
|
resolution: {integrity: sha512-dnrk87akzwRRKxS18dec2rY2ZvfIpvNq9SIHEPCCk2UNLI2R66HUO5v1RhLi6/oglbWtizcqzQWCa/DjjY1ljA==}
|
||||||
dependencies:
|
dependencies:
|
||||||
'@blocksuite/blocks': 0.3.0-alpha.8
|
'@blocksuite/blocks': 0.3.0-alpha.9
|
||||||
'@blocksuite/store': 0.3.0-alpha.8
|
'@blocksuite/store': 0.3.0-alpha.9
|
||||||
lit: 2.4.0
|
lit: 2.4.0
|
||||||
marked: 4.1.1
|
marked: 4.1.1
|
||||||
turndown: 7.1.1
|
turndown: 7.1.1
|
||||||
@@ -217,8 +217,8 @@ packages:
|
|||||||
react: 18.2.0
|
react: 18.2.0
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/@blocksuite/store/0.3.0-alpha.8:
|
/@blocksuite/store/0.3.0-alpha.9:
|
||||||
resolution: {integrity: sha512-u63FWZSiYID8++tEIaQZ5FSj7NZu9XM2ZdTaTU+7PDEwucK5Gqo8+YrEC9gVl+2jkSW5cp43pxf2kZolUgkyYg==}
|
resolution: {integrity: sha512-5wdnE6JUL1/y/LufqcRg2lRL5mYvdXc+MOj98eNfFAv+JFbY4vuGZ8N9HFn6dQ/rQaQsgaoAZkWyogbbC8x4IA==}
|
||||||
dependencies:
|
dependencies:
|
||||||
buffer: 6.0.3
|
buffer: 6.0.3
|
||||||
flexsearch: 0.7.21
|
flexsearch: 0.7.21
|
||||||
|
|||||||
Reference in New Issue
Block a user