Merge pull request #47 from toeverything/feat/layout

Feat/layout
This commit is contained in:
Qi
2022-10-24 16:10:08 +08:00
committed by GitHub
11 changed files with 196 additions and 82 deletions

View File

@@ -1,3 +1,4 @@
import { useState, useEffect } from 'react';
import { import {
StyledEdgelessToolbar, StyledEdgelessToolbar,
StyledToolbarWrapper, StyledToolbarWrapper,
@@ -19,59 +20,101 @@ import { useEditor } from '@/components/editor-provider';
const toolbarList1 = [ const toolbarList1 = [
{ {
flavor: 'select',
icon: <SelectIcon />, icon: <SelectIcon />,
toolTip: 'Select', toolTip: 'Select',
onClick: () => {},
disable: false, disable: false,
}, },
{ {
flavor: 'text',
icon: <TextIcon />, icon: <TextIcon />,
toolTip: 'Text(coming soon)', toolTip: 'Text(coming soon)',
onClick: () => {},
disable: true, disable: true,
}, },
{ {
flavor: 'shape',
icon: <ShapeIcon />, icon: <ShapeIcon />,
toolTip: 'Shape(coming soon)', toolTip: 'Shape(coming soon)',
onClick: () => {},
disable: true, disable: true,
}, },
{ {
flavor: 'sticky',
icon: <StickerIcon />, icon: <StickerIcon />,
toolTip: 'Sticker(coming soon)', toolTip: 'Sticky(coming soon)',
onClick: () => {},
disable: true, disable: true,
}, },
{ {
flavor: 'pen',
icon: <PenIcon />, icon: <PenIcon />,
toolTip: 'Pen(coming soon)', toolTip: 'Pen(coming soon)',
onClick: () => {},
disable: true, disable: true,
}, },
{ {
flavor: 'connector',
icon: <ConnectorIcon />, icon: <ConnectorIcon />,
toolTip: 'Connector(coming soon)', toolTip: 'Connector(coming soon)',
onClick: () => {},
disable: true, disable: true,
}, },
]; ];
const toolbarList2 = [ const toolbarList2 = [
{ {
flavor: 'undo',
icon: <UndoIcon />, icon: <UndoIcon />,
toolTip: 'Undo(coming soon)', toolTip: 'Undo',
onClick: () => {}, disable: false,
disable: true,
}, },
{ {
flavor: 'redo',
icon: <RedoIcon />, icon: <RedoIcon />,
toolTip: 'Redo(coming soon)', toolTip: 'Redo',
onClick: () => {}, disable: false,
disable: true,
}, },
]; ];
const UndoRedo = () => {
const [canUndo, setCanUndo] = useState(false);
const [canRedo, setCanRedo] = useState(false);
const { editor } = useEditor();
useEffect(() => {
if (!editor) return;
const { store } = editor;
store.signals.historyUpdated.on(() => {
setCanUndo(store.canUndo);
setCanRedo(store.canRedo);
});
}, [editor]);
return (
<StyledToolbarWrapper>
<Tooltip content="undo" placement="right-start">
<StyledToolbarItem
disable={!canUndo}
onClick={() => {
editor?.store?.undo();
}}
>
<UndoIcon />
</StyledToolbarItem>
</Tooltip>
<Tooltip content="redo" placement="right-start">
<StyledToolbarItem
disable={!canRedo}
onClick={() => {
editor?.store?.redo();
}}
>
<RedoIcon />
</StyledToolbarItem>
</Tooltip>
</StyledToolbarWrapper>
);
};
export const EdgelessToolbar = () => { export const EdgelessToolbar = () => {
const { mode } = useEditor(); const { mode } = useEditor();
return ( return (
<Slide <Slide
direction="right" direction="right"
@@ -81,27 +124,22 @@ export const EdgelessToolbar = () => {
> >
<StyledEdgelessToolbar> <StyledEdgelessToolbar>
<StyledToolbarWrapper> <StyledToolbarWrapper>
{toolbarList1.map(({ icon, toolTip, onClick, disable }, index) => { {toolbarList1.map(({ icon, toolTip, flavor, disable }, index) => {
return ( return (
<Tooltip key={index} content={toolTip} placement="right-start"> <Tooltip key={index} content={toolTip} placement="right-start">
<StyledToolbarItem disable={disable} onClick={onClick}> <StyledToolbarItem
{icon} disable={disable}
</StyledToolbarItem> onClick={() => {
</Tooltip> console.log('flavor', flavor);
); }}
})} >
</StyledToolbarWrapper>
<StyledToolbarWrapper>
{toolbarList2.map(({ icon, toolTip, onClick, disable }, index) => {
return (
<Tooltip key={index} content={toolTip} placement="right-start">
<StyledToolbarItem disable={disable} onClick={onClick}>
{icon} {icon}
</StyledToolbarItem> </StyledToolbarItem>
</Tooltip> </Tooltip>
); );
})} })}
</StyledToolbarWrapper> </StyledToolbarWrapper>
<UndoRedo />
</StyledEdgelessToolbar> </StyledEdgelessToolbar>
</Slide> </Slide>
); );

View File

@@ -20,22 +20,22 @@ export const StyledToolbarWrapper = styled.div(({ theme }) => ({
marginBottom: '12px', marginBottom: '12px',
})); }));
export const StyledToolbarItem = styled.div<{ disable: boolean }>( export const StyledToolbarItem = styled.div<{
({ theme, disable }) => ({ disable?: boolean;
}>(({ theme, disable = false }) => ({
width: '36px',
height: '36px',
...displayFlex('center', 'center'),
color: disable ? '#C0C0C0' : theme.colors.iconColor,
cursor: 'pointer',
svg: {
width: '36px', width: '36px',
height: '36px', height: '36px',
...displayFlex('center', 'center'), },
color: disable ? '#C0C0C0' : theme.colors.iconColor, ':hover': disable
cursor: 'pointer', ? {}
svg: { : {
width: '36px', color: theme.colors.primaryColor,
height: '36px', background: theme.colors.hoverBackground,
}, },
':hover': disable }));
? {}
: {
color: theme.colors.primaryColor,
background: theme.colors.hoverBackground,
},
})
);

View File

@@ -3,7 +3,7 @@ import { displayFlex, keyframes, styled } from '@/styles';
import spring, { toString } from 'css-spring'; import spring, { toString } from 'css-spring';
import type { ItemStatus } from './type'; import type { ItemStatus } from './type';
const ANIMATE_DURATION = 400; const ANIMATE_DURATION = 500;
export const StyledAnimateRadioContainer = styled('div')<{ shrink: boolean }>( export const StyledAnimateRadioContainer = styled('div')<{ shrink: boolean }>(
({ shrink, theme }) => { ({ shrink, theme }) => {

View File

@@ -9,6 +9,7 @@ type EditorContextValue = {
setEditor: (editor: EditorContainer) => void; setEditor: (editor: EditorContainer) => void;
setMode: (mode: EditorContainer['mode']) => void; setMode: (mode: EditorContainer['mode']) => void;
}; };
type EditorContextProps = PropsWithChildren<{}>; type EditorContextProps = PropsWithChildren<{}>;
export const EditorContext = createContext<EditorContextValue>({ export const EditorContext = createContext<EditorContextValue>({

View File

@@ -6,26 +6,29 @@ import '@blocksuite/editor';
import '@blocksuite/blocks/style'; import '@blocksuite/blocks/style';
import { useEditor } from '@/components/editor-provider'; import { useEditor } from '@/components/editor-provider';
import pkg from '../../package.json'; import pkg from '../../package.json';
import exampleMarkdown from './example-markdown';
export const Editor = () => { export const Editor = () => {
const editorRef = useRef<EditorContainer>(); const editorRef = useRef<EditorContainer>();
const { setEditor } = useEditor(); const { setEditor } = useEditor();
useEffect(() => { useEffect(() => {
setEditor(editorRef.current!); setEditor(editorRef.current!);
const { store } = editorRef.current as EditorContainer; const { store } = editorRef.current as EditorContainer;
const version = pkg.dependencies['@blocksuite/editor'].substring(1);
const pageId = store.addBlock({ const pageId = store.addBlock({
flavour: 'page', flavour: 'page',
title: `BlockSuite live demo ${version}`, title: 'Welcome to the AFFiNE Alpha',
}); });
const groupId = store.addBlock({ flavour: 'group' }, pageId); const groupId = store.addBlock({ flavour: 'group' }, pageId);
// const text = new Text(store, 'Legend from here...');
const text = new Text(store, 'Legend from here...'); // store.addBlock({ flavour: 'paragraph', text }, groupId);
store.addBlock({ flavour: 'paragraph', text }, groupId); editorRef.current!.clipboard.importMarkdown(exampleMarkdown, `${groupId}`);
store.resetHistory(); store.resetHistory();
}, [setEditor]); }, [setEditor]);
useEffect(() => {
const version = pkg.dependencies['@blocksuite/editor'].substring(1);
console.log(`BlockSuite live demo ${version}`);
}, []);
return ( return (
<Suspense fallback={<div>Error!</div>}> <Suspense fallback={<div>Error!</div>}>
<editor-container ref={editorRef} /> <editor-container ref={editorRef} />

View File

@@ -0,0 +1,62 @@
const exampleMarkdown = `The [AFFiNE Alpha](https://pathfinder.affine.systems/) is here! 👏
**What's different in AFFiNE Alpha?**
1. A much ~smoother editing~ experience, with much ~greater stability~;
2. More complete ~Markdown~ support and improved ~keyboard shortcuts~;
3. New features such as ~dark mode~
* **Switch between view styles using the** ☀ **and** 🌙.
4. ~Clean and modern UI/UX~ design.
**Looking for Markdown syntax or keyboard shortcuts?**
* Find the (?) in the bottom right, then the ️, to view a full list. of \`Keyboard Shortcuts\`
**Have an enjoyable editing experience !!!** **😃**
Have some feedback or just want to get in touch? Use the (?), then 🎧 to get in touch and join our communities.
**Still in development**
There also somethings you should consider about this AFFiNE Alpha including some ~limitations~:
* Single page editing - currently editing multiple docs/pages is not supported;
* Changes are not automatically stored, to save changes you should export your data. Options can be found by going to the top right and finding the \`⋮\` icon.
* Without an import/open feature you are still able to access your data by copying it back in.
**Keyboard Shortcuts:**
1. Undo: \`⌘ + Z\` or \`Ctrl+Z\`
2. Redo: \`⌘ + ⇧ + Z\` or \`Ctrl+Shift+Z\`
3. Bold: \`⌘ + B\` or \`Ctrl+B\`
4. Italic: \`⌘ + I\` or \`Ctrl+I\`
5. Underline: \`⌘ + U\` or \`Ctrl+U\`
6. Strikethrough: \`⌘ + ⇧ + S\` or \`Ctrl+Shift+S\`
7. Inline code: \`⌘ + E\` or \`Ctrl+E\`
8. Link: \`⌘ + K\` or \`Ctrl+K\`
9. Body text: \`⌘ + ⌥ + 0\` or \`Ctrl+Shift+0\`
10. Heading 1: \`⌘ + ⌥ + 1\` or \`Ctrl+Shift+1\`
11. Heading 2: \`⌘ + ⌥ + 2\` or \`Ctrl+Shift+2\`
12. Heading 3: \`⌘ + ⌥ + 3\` or \`Ctrl+Shift+3\`
13. Heading 4: \`⌘ + ⌥ + 4\` or \`Ctrl+Shift+4\`
14. Heading 5: \`⌘ + ⌥ + 5\` or \`Ctrl+Shift+5\`
15. Heading 6: \`⌘ + ⌥ + 6\` or \`Ctrl+Shift+6\`
16. Increase indent: \`Tab\`
17. Reduce indent: \`⇧ + Tab\` or \`Shift+Tab\`
_Markdown Syntax:_
1. Bold: \`**text**\`
2. Italic: \`*text*\`
3. Underline: \`~text~\`
4. Strikethrough: \`~~text~~\`
5. Inline code: \`\` \`code\` \`\`
6. Heading 1: \`# text\`
7. Heading 2: \`## text\`
8. Heading 3: \`### text\`
9. Heading 4: \`#### text\`
10. Heading 5: \`##### text\`
11. Heading 6: \`###### text\`
`;
export default exampleMarkdown;

View File

@@ -1,5 +1,6 @@
import { styled } from '@/styles'; import { styled } from '@/styles';
// Inspired by https://codepen.io/graphilla/pen/rNvBMYY
const loadingItemSize = '40px'; const loadingItemSize = '40px';
export const StyledLoading = styled.div` export const StyledLoading = styled.div`
position: relative; position: relative;

View File

@@ -19,17 +19,17 @@ export const macKeyboardShortcuts = {
}; };
export const macMarkdownShortcuts = { export const macMarkdownShortcuts = {
Bold: '**Text** Space', Bold: '**Text** ',
Italic: '*Text* Space', Italic: '*Text* ',
Underline: '~Text~ Space', Underline: '~Text~ ',
Strikethrough: '~~Text~~ Space', Strikethrough: '~~Text~~ ',
'Inline code': '`Code` Space', 'Inline code': '`Code` ',
'Heading 1': '# Space', 'Heading 1': '# ',
'Heading 2': '## Space', 'Heading 2': '## ',
'Heading 3': '### Space', 'Heading 3': '### ',
'Heading 4': '#### Space', 'Heading 4': '#### ',
'Heading 5': '##### Space', 'Heading 5': '##### ',
'Heading 6': '###### Space', 'Heading 6': '###### ',
}; };
export const windowsKeyboardShortcuts = { export const windowsKeyboardShortcuts = {
@@ -52,15 +52,15 @@ export const windowsKeyboardShortcuts = {
'Reduce indent': 'Shift + Tab', 'Reduce indent': 'Shift + Tab',
}; };
export const winMarkdownShortcuts = { export const winMarkdownShortcuts = {
Bold: '**Text** Space', Bold: '**Text** ',
Italic: '*Text* Space', Italic: '*Text* ',
Underline: '~Text~ Space', Underline: '~Text~ ',
Strikethrough: '~~Text~~ Space', Strikethrough: '~~Text~~ ',
'Inline code': '`Code` Space', 'Inline code': '`Code` ',
'Heading 1': '# Space', 'Heading 1': '# ',
'Heading 2': '## Space', 'Heading 2': '## ',
'Heading 3': '### Space', 'Heading 3': '### ',
'Heading 4': '#### Space', 'Heading 4': '#### ',
'Heading 5': '##### Space', 'Heading 5': '##### ',
'Heading 6': '###### Space', 'Heading 6': '###### ',
}; };

View File

@@ -1,8 +1,9 @@
import { displayFlex, styled } from '@/styles'; import { displayFlex, styled } from '@/styles';
export const StyledShortcutsModal = styled.div(({ theme }) => ({ export const StyledShortcutsModal = styled.div(({ theme }) => ({
width: '268px', width: '288px',
height: '66vh', height: '74vh',
paddingBottom: '28px',
backgroundColor: theme.colors.popoverBackground, backgroundColor: theme.colors.popoverBackground,
boxShadow: theme.shadow.popover, boxShadow: theme.shadow.popover,
color: theme.colors.popoverColor, color: theme.colors.popoverColor,
@@ -17,8 +18,9 @@ export const StyledShortcutsModal = styled.div(({ theme }) => ({
})); }));
export const StyledTitle = styled.div(({ theme }) => ({ export const StyledTitle = styled.div(({ theme }) => ({
color: theme.colors.textColor, color: theme.colors.textColor,
fontWeight: '600', fontWeight: '500',
fontSize: theme.font.sm, fontSize: theme.font.sm,
height: '44px',
...displayFlex('center', 'center'), ...displayFlex('center', 'center'),
svg: { svg: {
width: '20px', width: '20px',
@@ -27,17 +29,17 @@ export const StyledTitle = styled.div(({ theme }) => ({
}, },
})); }));
export const StyledSubTitle = styled.div(({ theme }) => ({ export const StyledSubTitle = styled.div(({ theme }) => ({
color: theme.colors.textColor, color: theme.colors.popoverColor,
fontWeight: '500', fontWeight: '500',
fontSize: '12px', fontSize: theme.font.sm,
height: '36px', height: '34px',
lineHeight: '36px', lineHeight: '36px',
marginTop: '28px', marginTop: '28px',
padding: '0 16px', padding: '0 16px',
})); }));
export const StyledModalHeader = styled.div(({ theme }) => ({ export const StyledModalHeader = styled.div(({ theme }) => ({
...displayFlex('space-between', 'center'), ...displayFlex('space-between', 'center'),
height: '36px', paddingTop: '8px 4px 0 4px',
width: '100%', width: '100%',
padding: '8px 16px 0 16px', padding: '8px 16px 0 16px',
position: 'sticky', position: 'sticky',
@@ -49,9 +51,9 @@ export const StyledModalHeader = styled.div(({ theme }) => ({
})); }));
export const StyledListItem = styled.div(({ theme }) => ({ export const StyledListItem = styled.div(({ theme }) => ({
height: '32px', height: '34px',
...displayFlex('space-between', 'center'), ...displayFlex('space-between', 'center'),
fontSize: theme.font.xs, fontSize: theme.font.sm,
padding: '0 16px', padding: '0 16px',
})); }));

View File

@@ -14,6 +14,7 @@ export const lightTheme: AffineTheme = {
codeBackground: '#f2f5f9', codeBackground: '#f2f5f9',
textColor: '#3A4C5C', textColor: '#3A4C5C',
edgelessTextColor: '#3A4C5C',
iconColor: '#9096A5', iconColor: '#9096A5',
linkColor: '#6880FF', linkColor: '#6880FF',
linkColor2: '#6880FF', linkColor2: '#6880FF',
@@ -59,6 +60,7 @@ export const darkTheme: AffineTheme = {
codeBackground: '#505662', codeBackground: '#505662',
textColor: '#fff', textColor: '#fff',
edgelessTextColor: '#3A4C5C',
iconColor: '#9096A5', iconColor: '#9096A5',
linkColor: '#7D91FF', linkColor: '#7D91FF',
linkColor2: '#6880FF', linkColor2: '#6880FF',
@@ -91,6 +93,7 @@ export const globalThemeVariables: (
'--affine-code-background': theme.colors.codeBackground, '--affine-code-background': theme.colors.codeBackground,
'--affine-text-color': theme.colors.textColor, '--affine-text-color': theme.colors.textColor,
'--affine-edgeless-text-color': theme.colors.edgelessTextColor,
'--affine-link-color': theme.colors.linkColor, '--affine-link-color': theme.colors.linkColor,
// In dark mode, normal text`s (not bold) color // In dark mode, normal text`s (not bold) color
'--affine-link-color2': theme.colors.linkColor2, '--affine-link-color2': theme.colors.linkColor2,

View File

@@ -20,7 +20,10 @@ export interface AffineTheme {
hoverBackground: string; hoverBackground: string;
codeBackground: string; codeBackground: string;
// Use for the page`s text
textColor: string; textColor: string;
// Use for the editor`s text, because in edgeless mode text is different form other
edgelessTextColor: string;
linkColor: string; linkColor: string;
// In dark mode, normal text`s (not bold) color // In dark mode, normal text`s (not bold) color
linkColor2: string; linkColor2: string;
@@ -64,6 +67,7 @@ export interface AffineThemeCSSVariables {
'--affine-code-background': AffineTheme['colors']['codeBackground']; '--affine-code-background': AffineTheme['colors']['codeBackground'];
'--affine-text-color': AffineTheme['colors']['textColor']; '--affine-text-color': AffineTheme['colors']['textColor'];
'--affine-edgeless-text-color': AffineTheme['colors']['edgelessTextColor'];
'--affine-link-color': AffineTheme['colors']['linkColor']; '--affine-link-color': AffineTheme['colors']['linkColor'];
// In dark mode, normal text`s (not bold) color // In dark mode, normal text`s (not bold) color
'--affine-link-color2': AffineTheme['colors']['linkColor2']; '--affine-link-color2': AffineTheme['colors']['linkColor2'];