merge develop to fix/bugs

This commit is contained in:
QiShaoXuan
2022-09-16 15:48:45 +08:00
17 changed files with 504 additions and 487 deletions
@@ -1,6 +1,7 @@
import en from './en.json';
import es from './es.json';
import zh_Hans from './zh.json';
import zh_Hant from './zh-Hant.json';
export const LOCALES = [
{
@@ -15,6 +16,12 @@ export const LOCALES = [
originalName: '简体中文',
res: zh_Hans,
},
{
name: 'Traditional Chinese',
tag: 'zh-Hant',
originalName: '繁體中文',
res: zh_Hant,
},
{
name: 'Spanish',
tag: 'es',
@@ -0,0 +1,39 @@
{
"Blog": "博客",
"AboutUs": "關於我們",
"Open Source": "開源",
"Docs": "文檔",
"Feedback": "反饋",
"ContactUs": "聯繫我們",
"Privacy First": "隱私第一",
"Alternative": "的另一種選擇",
"Check GitHub": "在 Github 中查看",
"GitHub": "GitHub",
"Try it Online": "在線嘗試",
"language": "語言",
"description1": {
"part1": "Affine 是面向專業用戶的下一代協同知識庫",
"part2": "Affine 不僅僅集合着文檔、白板和表格。",
"part3": "隨心所欲切換構塊模式。",
"part4": "告別冗余,存儲數據一次就好,放在任何您想要的位置。"
},
"description2": {
"part1": "塑形您的頁面",
"part2": "隨時隨地,文檔、看板、數據庫都完全可用。真正“所見即所得”的數據處理環境。",
"part3": "所有頁面都配備文檔(紙張模式)和白板(無邊緣模式)視圖。"
},
"description3": {
"part1": "計畫您的任務",
"part2": "多視圖管理不再混亂。",
"part3": "既可使用 Markdown 設置 TODO,又可無縫在看板視圖中編輯。",
"part4": "管理多維表格本當易如反掌——攜手Affine,确实易如反掌"
},
"description4": {
"part1": "隱私第一,合作無間,毫不妥協。",
"part2": "我們不喜歡被軟件綁架,您也是。我們的任何努力雖建立在尊重用戶隱私基礎上,也游刃有餘。這解釋了為什麼我們毫不妥協。",
"part3": "您創造的數據屬於您,安全永久存儲在本地供您隨時調用。同時您也能盡享協同的便利,無需雲設置就能實時編輯、共享給他人。"
},
"BuildFor": "構建一個開放和語義化的未來",
"KeepUpdated": "持續更新在",
"Join": "加入我們的社群"
}
@@ -38,13 +38,13 @@ export const createEditor = (
views: {
[Protocol.Block.Type.page]: new PageBlock(),
[Protocol.Block.Type.reference]: new RefLinkBlock(),
[Protocol.Block.Type.code]: new CodeBlock(),
[Protocol.Block.Type.text]: new TextBlock(),
[Protocol.Block.Type.heading1]: new Heading1Block(),
[Protocol.Block.Type.heading2]: new Heading2Block(),
[Protocol.Block.Type.heading3]: new Heading3Block(),
[Protocol.Block.Type.quote]: new QuoteBlock(),
[Protocol.Block.Type.todo]: new TodoBlock(),
[Protocol.Block.Type.code]: new CodeBlock(),
// [Protocol.Block.Type.toc]: new TocBlock(),
[Protocol.Block.Type.file]: new FileBlock(),
[Protocol.Block.Type.image]: new ImageBlock(),
+2 -10
View File
@@ -39,14 +39,6 @@ type MenuItemsProps = {
export const CommonList = (props: MenuItemsProps) => {
const { items, currentItem, setCurrentItem, onSelected } = props;
// const JSONUnsupportedBlockTypes = useFlag('JSONUnsupportedBlockTypes', [
// 'page',
// ]);
// TODO Insert bidirectional link to be developed
const JSONUnsupportedBlockTypes = ['page'];
const usedItems = items.filter(item => {
return !JSONUnsupportedBlockTypes.includes(item?.content?.id);
});
return (
<div className={clsx(styles('root_container'), props.className)}>
<div
@@ -55,8 +47,8 @@ export const CommonList = (props: MenuItemsProps) => {
commonListContainer,
])}
>
{usedItems?.length ? (
usedItems.map((item, idx) => {
{items?.length ? (
items.map((item, idx) => {
if (item.renderCustom) {
return item.renderCustom(item);
} else if (item.block) {
+2 -1
View File
@@ -38,7 +38,8 @@
"react-window": "^1.8.7",
"slate": "^0.81.1",
"slate-react": "^0.81.0",
"style9": "^0.14.0"
"style9": "^0.14.0",
"html-escaper": "^3.0.3"
},
"devDependencies": {
"@types/codemirror": "^5.60.5",
@@ -171,7 +171,7 @@ export const CodeView = ({ block, editor }: CreateCodeView) => {
};
const handleLangChange = (lang: string) => {
block.setProperty('lang', lang);
setExtensions([langs[lang]()]);
langs[lang] && setExtensions([langs[lang]()]);
};
useEffect(() => {
handleLangChange(langType ? langType : DEFAULT_LANG);
@@ -5,10 +5,10 @@ import {
BlockEditor,
HTML2BlockResult,
} from '@toeverything/framework/virgo';
import { unescape } from 'html-escaper';
import {
Block2HtmlProps,
commonBlock2HtmlContent,
commonHTML2block,
} from '../../utils/commonBlockClip';
import { CodeView } from './CodeView';
@@ -34,12 +34,30 @@ export class CodeBlock extends BaseView {
element: Element;
editor: BlockEditor;
}): Promise<HTML2BlockResult> {
return commonHTML2block({
element,
editor,
type: this.type,
tagName: 'CODE',
});
// debugger;
if (element.tagName === 'CODE') {
return [
{
type: this.type,
properties: {
text: {
value: [
{
text: unescape(
(element as HTMLElement).innerText
),
},
],
},
lang:
element.classList[0] &&
element.classList[0].substr(9),
},
children: [],
},
];
}
return null;
}
override async block2html(props: Block2HtmlProps) {
@@ -168,7 +168,9 @@ export const ImageView = ({ block, editor }: ImageView) => {
<BlockPendantProvider editor={editor} block={block}>
<ImageBlock>
<div style={{ position: 'relative' }} ref={resizeBox}>
{!isSelect ? <ImageShade onClick={handleClick} /> : null}
{!isSelect && imgUrl ? (
<ImageShade onClick={handleClick} />
) : null}
{imgUrl ? (
<div
onMouseDown={e => {
@@ -1,17 +1,17 @@
import {
BaseView,
CreateView,
AsyncBlock,
HTML2BlockResult,
BlockEditor,
} from '@toeverything/framework/virgo';
import { Protocol } from '@toeverything/datasource/db-service';
import { TextView } from './TextView';
import {
AsyncBlock,
BaseView,
BlockEditor,
CreateView,
HTML2BlockResult,
} from '@toeverything/framework/virgo';
import {
Block2HtmlProps,
commonBlock2HtmlContent,
commonHTML2block,
} from '../../utils/commonBlockClip';
import { TextView } from './TextView';
export class TextBlock extends BaseView {
type = Protocol.Block.Type.text;
@@ -41,12 +41,12 @@ export class TextBlock extends BaseView {
tagName: [
'DIV',
'P',
'PRE',
// 'PRE',
'B',
'A',
'EM',
'U',
'CODE',
// 'CODE',
'S',
'DEL',
],
@@ -1,5 +1,3 @@
import { useState } from 'react';
import { CustomText, TextProps } from '@toeverything/components/common';
import {
BlockPendantProvider,
@@ -11,9 +9,11 @@ import {
import { styled } from '@toeverything/components/ui';
import { Protocol } from '@toeverything/datasource/db-service';
import { CreateView } from '@toeverything/framework/virgo';
import { useState } from 'react';
import { BlockContainer } from '../../components/BlockContainer';
import { TextManage } from '../../components/text-manage';
import { dedentBlock, tabBlock } from '../../utils/indent';
interface CreateTextView extends CreateView {
// TODO: need to optimize
containerClassName?: string;
@@ -247,6 +247,9 @@ export const TextView = ({
await block.setProperty('text', {
value: options?.['text'] as CustomText[],
});
setTimeout(async () => {
await editor.selectionManager.activeNodeByNodeId(block.id);
}, 100);
block.firstCreateFlag = true;
};
const onTab: TextProps['handleTab'] = async ({ isShiftKey }) => {
@@ -1,15 +1,15 @@
import { Protocol } from '@toeverything/datasource/db-service';
import { AsyncBlock } from '../block';
import { Editor } from '../editor';
import { SelectBlock, SelectInfo } from '../selection';
import { AsyncBlock } from '../block';
import { ClipBlockInfo, OFFICE_CLIPBOARD_MIMETYPE } from './types';
import { Clip } from './clip';
import { ClipBlockInfo, OFFICE_CLIPBOARD_MIMETYPE } from './types';
import {
commonHTML2Block,
commonHTML2Text,
getIsTextLink,
linkText2Block,
} from './utils';
export class ClipboardUtils {
private _editor: Editor;
constructor(editor: Editor) {
@@ -161,12 +161,37 @@ export class ClipboardUtils {
return this.convertHtml2Blocks(htmlEl);
}
async convertHtml2Blocks(element: Element): Promise<ClipBlockInfo[]> {
const editableViews = this._editor.getEditableViews();
// const editableViews = this._editor.getEditableViews();
// 如果block能够捕捉htmlElement则返回block的html2block
const CONVERT_SORT_LIST = [
Protocol.Block.Type.page,
Protocol.Block.Type.reference,
Protocol.Block.Type.code,
Protocol.Block.Type.text,
Protocol.Block.Type.heading1,
Protocol.Block.Type.heading2,
Protocol.Block.Type.heading3,
Protocol.Block.Type.quote,
Protocol.Block.Type.todo,
Protocol.Block.Type.file,
Protocol.Block.Type.image,
Protocol.Block.Type.divider,
Protocol.Block.Type.callout,
Protocol.Block.Type.youtube,
Protocol.Block.Type.figma,
Protocol.Block.Type.group,
Protocol.Block.Type.embedLink,
Protocol.Block.Type.numbered,
Protocol.Block.Type.bullet,
Protocol.Block.Type.grid,
Protocol.Block.Type.gridItem,
Protocol.Block.Type.groupDivider,
];
const [clipBlockInfos] = (
await Promise.all(
editableViews.map(editableView => {
return editableView?.html2block?.({
CONVERT_SORT_LIST.map(type => {
return this._editor.getView(type)?.html2block?.({
editor: this._editor,
element: element,
});
@@ -1,9 +1,8 @@
import { StrictMode } from 'react';
import { createRoot, type Root } from 'react-dom/client';
import { BasePlugin } from '../../base-plugin';
import { CommandMenu } from './Menu';
import { PluginRenderRoot } from '../../utils';
import { CommandMenu } from './Menu';
const PLUGIN_NAME = 'command-menu';
@@ -1,28 +1,24 @@
import React, {
useEffect,
useState,
useMemo,
useCallback,
useEffect,
useMemo,
useRef,
useState,
} from 'react';
import style9 from 'style9';
import { BlockFlavorKeys } from '@toeverything/datasource/db-service';
import { Virgo, PluginHooks, HookType } from '@toeverything/framework/virgo';
import {
CommonList,
CommonListItem,
commonListContainer,
CommonListItem,
} from '@toeverything/components/common';
import { BlockFlavorKeys } from '@toeverything/datasource/db-service';
import { HookType, PluginHooks, Virgo } from '@toeverything/framework/virgo';
import { domToRect } from '@toeverything/utils';
import { MenuCategories } from './Categories';
import { menuItemsMap, CommandMenuCategories } from './config';
import { styled } from '@toeverything/components/ui';
import { QueryResult } from '../../search';
import {
MuiClickAwayListener as ClickAwayListener,
styled,
} from '@toeverything/components/ui';
import { MenuCategories } from './Categories';
import { CommandMenuCategories, menuItemsMap } from './config';
const RootContainer = styled('div')(({ theme }) => {
return {
@@ -134,59 +130,75 @@ export const CommandMenuContainer = ({
const handleClickUp = useCallback(
(event: React.KeyboardEvent<HTMLDivElement>) => {
if (isShow && types && event.code === 'ArrowUp') {
event.preventDefault();
if (!currentItem && types.length) {
setCurrentItem(types[types.length - 1]);
}
if (currentItem) {
const idx = types.indexOf(currentItem);
if (idx > 0) {
setNeedCheckIntoView(true);
setCurrentItem(types[idx - 1]);
}
event.preventDefault();
if (!currentItem && types.length) {
setCurrentItem(types[types.length - 1]);
}
if (currentItem) {
const idx = types.indexOf(currentItem);
if (idx > 0) {
setNeedCheckIntoView(true);
setCurrentItem(types[idx - 1]);
}
}
},
[isShow, types, currentItem]
[types, currentItem]
);
const handleClickDown = useCallback(
(event: React.KeyboardEvent<HTMLDivElement>) => {
if (isShow && types && event.code === 'ArrowDown') {
event.preventDefault();
if (!currentItem && types.length) {
setCurrentItem(types[0]);
}
if (currentItem) {
const idx = types.indexOf(currentItem);
if (idx < types.length - 1) {
setNeedCheckIntoView(true);
setCurrentItem(types[idx + 1]);
}
event.preventDefault();
if (!currentItem && types.length) {
setCurrentItem(types[0]);
}
if (currentItem) {
const idx = types.indexOf(currentItem);
if (idx < types.length - 1) {
setNeedCheckIntoView(true);
setCurrentItem(types[idx + 1]);
}
}
},
[isShow, types, currentItem]
[types, currentItem]
);
const handleClickEnter = useCallback(
async (event: React.KeyboardEvent<HTMLDivElement>) => {
if (isShow && event.code === 'Enter' && currentItem) {
event.preventDefault();
onSelected && onSelected(currentItem);
}
event.preventDefault();
onSelected && onSelected(currentItem);
},
[isShow, currentItem, onSelected]
[currentItem, onSelected]
);
const handleKeyDown = useCallback(
(event: React.KeyboardEvent<HTMLDivElement>) => {
handleClickUp(event);
handleClickDown(event);
handleClickEnter(event);
if (!isShow) {
return;
}
if (event.nativeEvent.isComposing) {
return;
}
if (types && event.code === 'ArrowUp') {
handleClickUp(event);
return;
}
if (types && event.code === 'ArrowDown') {
handleClickDown(event);
return;
}
if (event.code === 'Enter' && currentItem) {
handleClickEnter(event);
return;
}
},
[handleClickUp, handleClickDown, handleClickEnter]
[
isShow,
types,
currentItem,
handleClickUp,
handleClickDown,
handleClickEnter,
]
);
useEffect(() => {
@@ -212,7 +212,7 @@ export const CommandMenu = ({ editor, hooks, style }: CommandMenuProps) => {
const handleSelected = async (type: BlockFlavorKeys | string) => {
const text = await editor.commands.textCommands.getBlockText(blockId);
const block = await editor.getBlockById(blockId);
let textValue = block.getProperty('text').value;
const textValue = block.getProperty('text').value;
editor.blockHelper.removeSearchSlash(blockId, true);
if (type.startsWith('Virgo')) {
const handler =
@@ -261,23 +261,21 @@ export const CommandMenu = ({ editor, hooks, style }: CommandMenuProps) => {
>
{show ? (
<MuiClickAwayListener onClickAway={handleClickAway}>
<div>
<CommandMenuContainer
editor={editor}
hooks={hooks}
style={{
...commandMenuPosition,
...style,
}}
isShow={show}
blockId={blockId}
onSelected={handleSelected}
onclose={handleClose}
searchBlocks={searchBlocks}
types={types}
categories={categories}
/>
</div>
<CommandMenuContainer
editor={editor}
hooks={hooks}
style={{
...commandMenuPosition,
...style,
}}
isShow={show}
blockId={blockId}
onSelected={handleSelected}
onclose={handleClose}
searchBlocks={searchBlocks}
types={types}
categories={categories}
/>
</MuiClickAwayListener>
) : (
<></>
@@ -1,25 +1,24 @@
import {
HeadingOneIcon,
HeadingTwoIcon,
HeadingThreeIcon,
ToDoIcon,
NumberIcon,
BulletIcon,
CodeIcon,
TextIcon,
PagesIcon,
ImageIcon,
FileIcon,
QuoteIcon,
CalloutIcon,
CodeIcon,
DividerIcon,
FigmaIcon,
YoutubeIcon,
EmbedIcon,
FigmaIcon,
FileIcon,
HeadingOneIcon,
HeadingThreeIcon,
HeadingTwoIcon,
ImageIcon,
NumberIcon,
QuoteIcon,
TextIcon,
ToDoIcon,
YoutubeIcon,
} from '@toeverything/components/icons';
import { SvgIconProps } from '@toeverything/components/ui';
import { Virgo } from '@toeverything/framework/virgo';
import { BlockFlavorKeys, Protocol } from '@toeverything/datasource/db-service';
import { Virgo } from '@toeverything/framework/virgo';
import { without } from '@toeverything/utils';
export enum CommandMenuCategories {
@@ -68,11 +67,12 @@ export const menuItemsMap: {
type: Protocol.Block.Type.text,
icon: TextIcon,
},
{
text: 'Page',
type: 'page',
icon: PagesIcon,
},
// the Page block should not be inserted
// {
// text: 'Page',
// type: Protocol.Block.Type.page,
// icon: PagesIcon,
// },
{
text: 'Heading 1',
type: Protocol.Block.Type.heading1,
@@ -91,7 +91,7 @@ export const menuItemsMap: {
],
[CommandMenuCategories.lists]: [
{
text: 'To do',
text: 'Todo',
type: Protocol.Block.Type.todo,
icon: ToDoIcon,
},
@@ -22,7 +22,6 @@ import {
HeadingTwoIcon,
ImageIcon,
LinkIcon,
MoreIcon,
NumberIcon,
PagesIcon,
QuoteIcon,
@@ -827,13 +826,13 @@ export const getInlineMenuData = ({
nameKey: inlineMenuNamesKeys.backlinks,
onClick: common_handler_for_inline_menu,
},
{
type: INLINE_MENU_UI_TYPES.icon,
icon: MoreIcon,
name: inlineMenuNames.moreActions,
nameKey: inlineMenuNamesKeys.moreActions,
onClick: common_handler_for_inline_menu,
},
// {
// type: INLINE_MENU_UI_TYPES.icon,
// icon: MoreIcon,
// name: inlineMenuNames.moreActions,
// nameKey: inlineMenuNamesKeys.moreActions,
// onClick: common_handler_for_inline_menu,
// },
];
return inlineMenuData.filter(item => Boolean(item));
+276 -354
View File
File diff suppressed because it is too large Load Diff