From 012c0441d0e96a4b6f2dead990ab69a32d064ad8 Mon Sep 17 00:00:00 2001 From: QiShaoXuan Date: Wed, 24 Aug 2022 14:41:21 +0800 Subject: [PATCH] refactor: recode html2block --- .../editor-blocks/src/blocks/bullet/index.ts | 78 +++--- .../editor-blocks/src/blocks/code/index.ts | 54 ++-- .../editor-blocks/src/blocks/divider/index.ts | 36 +-- .../src/blocks/embed-link/index.ts | 23 -- .../editor-blocks/src/blocks/figma/index.ts | 29 --- .../src/blocks/groupDvider/index.ts | 18 -- .../editor-blocks/src/blocks/image/index.ts | 34 ++- .../src/blocks/numbered/index.ts | 83 +++--- .../src/blocks/text/QuoteBlock.tsx | 105 +++----- .../src/blocks/text/TextBlock.tsx | 246 +++++------------- .../editor-blocks/src/blocks/todo/index.ts | 84 +++--- .../editor-blocks/src/blocks/youtube/index.ts | 29 --- .../src/utils/commonBlockClip.ts | 27 ++ libs/components/editor-core/package.json | 2 + .../src/editor/clipboard/clipboard-parse.ts | 134 ---------- .../src/editor/clipboard/clipboard.ts | 14 +- .../src/editor/clipboard/clipboardUtils.ts | 60 +++++ .../editor-core/src/editor/clipboard/copy.ts | 6 - .../editor-core/src/editor/clipboard/paste.ts | 172 +++++------- .../editor-core/src/editor/clipboard/types.ts | 7 +- .../editor-core/src/editor/clipboard/utils.ts | 150 +++++++++++ .../editor-core/src/editor/editor.ts | 8 + .../editor-core/src/editor/index.ts | 5 +- .../editor-core/src/editor/views/base-view.ts | 16 +- pnpm-lock.yaml | 101 +++++++ 25 files changed, 693 insertions(+), 828 deletions(-) delete mode 100644 libs/components/editor-core/src/editor/clipboard/clipboard-parse.ts create mode 100644 libs/components/editor-core/src/editor/clipboard/utils.ts diff --git a/libs/components/editor-blocks/src/blocks/bullet/index.ts b/libs/components/editor-blocks/src/blocks/bullet/index.ts index 1577263af3..212575da31 100644 --- a/libs/components/editor-blocks/src/blocks/bullet/index.ts +++ b/libs/components/editor-blocks/src/blocks/bullet/index.ts @@ -1,9 +1,15 @@ -import { AsyncBlock, BaseView } from '@toeverything/framework/virgo'; +import { + AsyncBlock, + BaseView, + BlockEditor, + HTML2BlockResult, +} from '@toeverything/framework/virgo'; import { Protocol } from '@toeverything/datasource/db-service'; import { defaultBulletProps, BulletView } from './BulletView'; import { Block2HtmlProps, commonBlock2HtmlContent, + commonHTML2block, } from '../../utils/commonBlockClip'; export class BulletBlock extends BaseView { public type = Protocol.Block.Type.bullet; @@ -18,49 +24,41 @@ export class BulletBlock extends BaseView { } return block; } + override async html2block2({ + element, + editor, + }: { + element: Element; + editor: BlockEditor; + }): Promise { + if (element.tagName === 'UL') { + const firstList = element.querySelector('li'); - override html2block( - el: Element, - parseEl: (el: Element) => any[] - ): any[] | null { - const tag_name = el.tagName; - if (tag_name === 'UL') { - const result = []; - for (let i = 0; i < el.children.length; i++) { - const blocks_info = parseEl(el.children[i]); - result.push(...blocks_info); + if (!firstList || firstList.innerText.startsWith('[ ] ')) { + return null; } - return result.length > 0 ? result : null; + const children = Array.from(element.children); + const childrenBlockInfos = ( + await Promise.all( + children.map(childElement => + this.html2block2({ + editor, + element: childElement, + }) + ) + ) + ) + .flat() + .filter(v => v); + return childrenBlockInfos.length ? childrenBlockInfos : null; } - if (tag_name == 'LI' && !el.textContent.startsWith('[ ] ')) { - const childNodes = el.childNodes; - const texts = []; - const children = []; - for (let i = 0; i < childNodes.length; i++) { - const blocks_info = parseEl(childNodes[i] as Element); - for (let j = 0; j < blocks_info.length; j++) { - if (blocks_info[j].type === 'text') { - const block_texts = - blocks_info[j].properties.text.value; - texts.push(...block_texts); - } else { - children.push(blocks_info[j]); - } - } - } - return [ - { - type: this.type, - properties: { - text: { value: texts }, - }, - children: children, - }, - ]; - } - - return null; + return commonHTML2block({ + element, + editor, + type: this.type, + tagName: 'LI', + }); } override async block2html(props: Block2HtmlProps) { diff --git a/libs/components/editor-blocks/src/blocks/code/index.ts b/libs/components/editor-blocks/src/blocks/code/index.ts index a204779656..306d909716 100644 --- a/libs/components/editor-blocks/src/blocks/code/index.ts +++ b/libs/components/editor-blocks/src/blocks/code/index.ts @@ -1,9 +1,15 @@ -import { BaseView, AsyncBlock } from '@toeverything/framework/virgo'; +import { + BaseView, + AsyncBlock, + BlockEditor, + HTML2BlockResult, +} from '@toeverything/framework/virgo'; import { Protocol } from '@toeverything/datasource/db-service'; import { CodeView } from './CodeView'; import { Block2HtmlProps, commonBlock2HtmlContent, + commonHTML2block, } from '../../utils/commonBlockClip'; export class CodeBlock extends BaseView { @@ -21,39 +27,19 @@ export class CodeBlock extends BaseView { return block; } - // TODO: internal format not implemented yet - override html2block( - el: Element, - parseEl: (el: Element) => any[] - ): any[] | null { - const tag_name = el.tagName; - if (tag_name === 'CODE') { - const childNodes = el.childNodes; - let text_value = ''; - for (let i = 0; i < childNodes.length; i++) { - const blocks_info = parseEl(childNodes[i] as Element); - for (let j = 0; j < blocks_info.length; j++) { - if (blocks_info[j].type === 'text') { - const block_texts = - blocks_info[j].properties.text.value; - if (block_texts.length > 0) { - text_value += block_texts[0].text; - } - } - } - } - return [ - { - type: this.type, - properties: { - text: { value: [{ text: text_value }] }, - }, - children: [], - }, - ]; - } - - return null; + override async html2block2({ + element, + editor, + }: { + element: Element; + editor: BlockEditor; + }): Promise { + return commonHTML2block({ + element, + editor, + type: this.type, + tagName: 'CODE', + }); } override async block2html(props: Block2HtmlProps) { diff --git a/libs/components/editor-blocks/src/blocks/divider/index.ts b/libs/components/editor-blocks/src/blocks/divider/index.ts index eb4615963c..14f29b8807 100644 --- a/libs/components/editor-blocks/src/blocks/divider/index.ts +++ b/libs/components/editor-blocks/src/blocks/divider/index.ts @@ -1,34 +1,34 @@ import { AsyncBlock, BaseView, + BlockEditor, + HTML2BlockResult, SelectBlock, } from '@toeverything/framework/virgo'; import { Protocol } from '@toeverything/datasource/db-service'; import { DividerView } from './divider-view'; -import { Block2HtmlProps } from '../../utils/commonBlockClip'; +import { Block2HtmlProps, commonHTML2block } from '../../utils/commonBlockClip'; export class DividerBlock extends BaseView { type = Protocol.Block.Type.divider; View = DividerView; - override html2block( - el: Element, - parseEl: (el: Element) => any[] - ): any[] | null { - const tag_name = el.tagName; - if (tag_name === 'HR') { - return [ - { - type: this.type, - properties: { - text: {}, - }, - children: [], - }, - ]; - } - return null; + override async html2block2({ + element, + editor, + }: { + element: Element; + editor: BlockEditor; + }): Promise { + return commonHTML2block({ + element, + editor, + type: this.type, + tagName: 'HR', + ignoreEmptyElement: false, + }); } + override async block2html(props: Block2HtmlProps) { return `
`; } diff --git a/libs/components/editor-blocks/src/blocks/embed-link/index.ts b/libs/components/editor-blocks/src/blocks/embed-link/index.ts index 7674c71c3e..8b6e23f527 100644 --- a/libs/components/editor-blocks/src/blocks/embed-link/index.ts +++ b/libs/components/editor-blocks/src/blocks/embed-link/index.ts @@ -13,29 +13,6 @@ export class EmbedLinkBlock extends BaseView { type = Protocol.Block.Type.embedLink; View = EmbedLinkView; - override html2block( - el: Element, - parseEl: (el: Element) => any[] - ): any[] | null { - const tag_name = el.tagName; - if (tag_name === 'A' && el.parentElement?.childElementCount === 1) { - return [ - { - type: this.type, - properties: { - // TODO: Not sure what value to fill for name - embedLink: { - name: this.type, - value: el.getAttribute('href'), - }, - }, - children: [], - }, - ]; - } - - return null; - } override async block2html({ block }: Block2HtmlProps) { const url = block.getProperty('embedLink')?.value; return `

${url}

`; diff --git a/libs/components/editor-blocks/src/blocks/figma/index.ts b/libs/components/editor-blocks/src/blocks/figma/index.ts index 652abfbc1d..382429b137 100644 --- a/libs/components/editor-blocks/src/blocks/figma/index.ts +++ b/libs/components/editor-blocks/src/blocks/figma/index.ts @@ -13,35 +13,6 @@ export class FigmaBlock extends BaseView { type = Protocol.Block.Type.figma; View = FigmaView; - override html2block( - el: Element, - parseEl: (el: Element) => any[] - ): any[] | null { - const tag_name = el.tagName; - if (tag_name === 'A' && el.parentElement?.childElementCount === 1) { - const href = el.getAttribute('href'); - const allowedHosts = ['www.figma.com']; - const host = new URL(href).host; - - if (allowedHosts.includes(host)) { - return [ - { - type: this.type, - properties: { - // TODO: Not sure what value to fill for name - embedLink: { - name: this.type, - value: el.getAttribute('href'), - }, - }, - children: [], - }, - ]; - } - } - - return null; - } override async block2html({ block }: Block2HtmlProps) { const figmaUrl = block.getProperty('embedLink')?.value; return `

${figmaUrl}

`; diff --git a/libs/components/editor-blocks/src/blocks/groupDvider/index.ts b/libs/components/editor-blocks/src/blocks/groupDvider/index.ts index 52940173e6..458ef2f708 100644 --- a/libs/components/editor-blocks/src/blocks/groupDvider/index.ts +++ b/libs/components/editor-blocks/src/blocks/groupDvider/index.ts @@ -10,25 +10,7 @@ import { Block2HtmlProps } from '../../utils/commonBlockClip'; export class GroupDividerBlock extends BaseView { type = Protocol.Block.Type.groupDivider; View = GroupDividerView; - override html2block( - el: Element, - parseEl: (el: Element) => any[] - ): any[] | null { - const tag_name = el.tagName; - if (tag_name === 'HR') { - return [ - { - type: this.type, - properties: { - text: {}, - }, - children: [], - }, - ]; - } - return null; - } override async block2html(props: Block2HtmlProps) { return `
`; } diff --git a/libs/components/editor-blocks/src/blocks/image/index.ts b/libs/components/editor-blocks/src/blocks/image/index.ts index 190f83d72a..844ae43cb2 100644 --- a/libs/components/editor-blocks/src/blocks/image/index.ts +++ b/libs/components/editor-blocks/src/blocks/image/index.ts @@ -1,7 +1,12 @@ -import { BaseView } from '@toeverything/framework/virgo'; +import { + BaseView, + BlockEditor, + HTML2BlockResult, +} from '@toeverything/framework/virgo'; import { Protocol } from '@toeverything/datasource/db-service'; import { ImageView } from './ImageView'; import { Block2HtmlProps } from '../../utils/commonBlockClip'; +import { getRandomString } from '@toeverything/components/common'; export class ImageBlock extends BaseView { public override selectable = true; @@ -10,27 +15,30 @@ export class ImageBlock extends BaseView { View = ImageView; // TODO: needs to download the image and then upload it to get a new link and then assign it - override html2block( - el: Element, - parseEl: (el: Element) => any[] - ): any[] | null { - const tag_name = el.tagName; - if (tag_name === 'IMG') { + override async html2block2({ + element, + editor, + }: { + element: Element; + editor: BlockEditor; + }): Promise { + if (element.tagName === 'IMG') { return [ { type: this.type, properties: { - value: '', - url: el.getAttribute('src'), - name: el.getAttribute('src'), - size: 0, - type: 'link', + image: { + value: getRandomString('image'), + url: element.getAttribute('src'), + name: element.getAttribute('src'), + size: 0, + type: 'link', + }, }, children: [], }, ]; } - return null; } diff --git a/libs/components/editor-blocks/src/blocks/numbered/index.ts b/libs/components/editor-blocks/src/blocks/numbered/index.ts index d6eb9c1839..5c3f8a4e4e 100644 --- a/libs/components/editor-blocks/src/blocks/numbered/index.ts +++ b/libs/components/editor-blocks/src/blocks/numbered/index.ts @@ -1,9 +1,15 @@ -import { AsyncBlock, BaseView } from '@toeverything/framework/virgo'; +import { + AsyncBlock, + BaseView, + BlockEditor, + HTML2BlockResult, +} from '@toeverything/framework/virgo'; import { Protocol } from '@toeverything/datasource/db-service'; import { defaultTodoProps, NumberedView } from './NumberedView'; import { Block2HtmlProps, commonBlock2HtmlContent, + commonHTML2block, } from '../../utils/commonBlockClip'; export class NumberedBlock extends BaseView { @@ -22,55 +28,38 @@ export class NumberedBlock extends BaseView { return block; } - override html2block( - el: Element, - parseEl: (el: Element) => any[] - ): any[] | null { - const tag_name = el.tagName; - if (tag_name === 'OL') { - const result = []; - for (let i = 0; i < el.children.length; i++) { - const blocks_info = parseEl(el.children[i]); - result.push(...blocks_info); - } - return result.length > 0 ? result : null; + override async html2block2({ + element, + editor, + }: { + element: Element; + editor: BlockEditor; + }): Promise { + if (element.tagName === 'OL') { + const children = Array.from(element.children); + const childrenBlockInfos = ( + await Promise.all( + children.map(childElement => + this.html2block2({ + editor, + element: childElement, + }) + ) + ) + ) + .flat() + .filter(v => v); + return childrenBlockInfos.length ? childrenBlockInfos : null; } - if (tag_name == 'LI' && el.textContent.startsWith('[ ] ')) { - const childNodes = el.childNodes; - let texts = []; - const children = []; - for (let i = 0; i < childNodes.length; i++) { - const blocks_info = parseEl(childNodes[i] as Element); - for (let j = 0; j < blocks_info.length; j++) { - if (blocks_info[j].type === 'text') { - const block_texts = - blocks_info[j].properties.text.value; - texts.push(...block_texts); - } else { - children.push(blocks_info[j]); - } - } - } - if (texts.length > 0 && (texts[0].text || '').startsWith('[ ] ')) { - texts[0].text = texts[0].text.substring('[ ] '.length); - if (!texts[0].text) { - texts = texts.slice(1); - } - } - return [ - { - type: this.type, - properties: { - text: { value: texts }, - }, - children: children, - }, - ]; - } - - return null; + return commonHTML2block({ + element, + editor, + type: this.type, + tagName: 'LI', + }); } + override async block2html(props: Block2HtmlProps) { return `
  1. ${await commonBlock2HtmlContent(props)}
`; } diff --git a/libs/components/editor-blocks/src/blocks/text/QuoteBlock.tsx b/libs/components/editor-blocks/src/blocks/text/QuoteBlock.tsx index c3613f585d..51f63e3dec 100644 --- a/libs/components/editor-blocks/src/blocks/text/QuoteBlock.tsx +++ b/libs/components/editor-blocks/src/blocks/text/QuoteBlock.tsx @@ -2,11 +2,14 @@ import { Protocol } from '@toeverything/datasource/db-service'; import { AsyncBlock, BaseView, + BlockEditor, CreateView, + HTML2BlockResult, } from '@toeverything/framework/virgo'; import { Block2HtmlProps, commonBlock2HtmlContent, + commonHTML2block, } from '../../utils/commonBlockClip'; import { TextView } from './TextView'; @@ -27,36 +30,19 @@ export class QuoteBlock extends BaseView { return block; } - override html2block( - el: Element, - parseEl: (el: Element) => any[] - ): any[] | null { - const tag_name = el.tagName; - if (tag_name === 'BLOCKQUOTE') { - const childNodes = el.childNodes; - const texts = []; - for (let i = 0; i < childNodes.length; i++) { - const blocks_info = parseEl(childNodes[i] as Element); - for (let j = 0; j < blocks_info.length; j++) { - if (blocks_info[j].type === 'text') { - const block_texts = - blocks_info[j].properties.text.value; - texts.push(...block_texts); - } - } - } - return [ - { - type: this.type, - properties: { - text: { value: texts }, - }, - children: [], - }, - ]; - } - - return null; + override async html2block2({ + element, + editor, + }: { + element: Element; + editor: BlockEditor; + }): Promise { + return commonHTML2block({ + element, + editor, + type: this.type, + tagName: 'BLOCKQUOTE', + }); } override async block2html(props: Block2HtmlProps) { @@ -82,52 +68,19 @@ export class CalloutBlock extends BaseView { return block; } - override html2block( - el: Element, - parseEl: (el: Element) => any[] - ): any[] | null { - const tag_name = el.tagName; - if ( - tag_name === 'ASIDE' || - el.firstChild?.nodeValue?.startsWith('