From aa877b64911fb2dbe0d85c3ae20470fa6842799e Mon Sep 17 00:00:00 2001 From: QiShaoXuan Date: Tue, 16 Aug 2022 17:28:40 +0800 Subject: [PATCH 1/9] fix: the pendant popover is blocked by the container in kanban mode --- .../editor-core/src/block-pendant/BlockPendantProvider.tsx | 2 +- .../block-pendant/pendant-history-panel/PendantHistoryPanel.tsx | 2 +- .../src/block-pendant/pendant-render/PandentRender.tsx | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/libs/components/editor-core/src/block-pendant/BlockPendantProvider.tsx b/libs/components/editor-core/src/block-pendant/BlockPendantProvider.tsx index 955cbc5b9f..6d099d8ae4 100644 --- a/libs/components/editor-core/src/block-pendant/BlockPendantProvider.tsx +++ b/libs/components/editor-core/src/block-pendant/BlockPendantProvider.tsx @@ -31,7 +31,7 @@ export const BlockPendantProvider = ({ diff --git a/libs/components/editor-core/src/block-pendant/pendant-history-panel/PendantHistoryPanel.tsx b/libs/components/editor-core/src/block-pendant/pendant-history-panel/PendantHistoryPanel.tsx index 3efc36721f..d66aa0a706 100644 --- a/libs/components/editor-core/src/block-pendant/pendant-history-panel/PendantHistoryPanel.tsx +++ b/libs/components/editor-core/src/block-pendant/pendant-history-panel/PendantHistoryPanel.tsx @@ -117,7 +117,7 @@ export const PendantHistoryPanel = ({ /> } trigger="click" - container={historyPanelRef.current} + // container={historyPanelRef.current} > { popperHandlerRef={ref => { popoverHandlerRef.current[id] = ref; }} - container={blockRenderContainerRef.current} + // container={blockRenderContainerRef.current} key={id} trigger="click" placement="bottom-start" From fcabeb919e53a27b566050930c47ffe3f4c8f30d Mon Sep 17 00:00:00 2001 From: QiShaoXuan Date: Wed, 17 Aug 2022 11:51:03 +0800 Subject: [PATCH 2/9] fix: fix not being able to paste a block with children in it --- .../editor-core/src/editor/clipboard/paste.ts | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/libs/components/editor-core/src/editor/clipboard/paste.ts b/libs/components/editor-core/src/editor/clipboard/paste.ts index b24d06b23a..8b5ed40ee5 100644 --- a/libs/components/editor-core/src/editor/clipboard/paste.ts +++ b/libs/components/editor-core/src/editor/clipboard/paste.ts @@ -377,15 +377,16 @@ export class Paste { const pasteBlocks = await this._createBlocks(blocks); let groupBlock: AsyncBlock; - if ( - selectedBlock?.type === 'group' || - selectedBlock?.type === 'page' - ) { + if (selectedBlock?.type === 'page') { groupBlock = await this._editor.createBlock('group'); await Promise.all( pasteBlocks.map(block => groupBlock.append(block)) ); await selectedBlock.after(groupBlock); + } else if (selectedBlock?.type === 'group') { + await Promise.all( + pasteBlocks.map(block => selectedBlock.append(block)) + ); } else { await Promise.all( pasteBlocks.map(block => selectedBlock.after(block)) @@ -413,7 +414,11 @@ export class Paste { clipBlockInfo.type ); block?.setProperties(clipBlockInfo.properties); - await this._createBlocks(clipBlockInfo.children, block?.id); + const children = await this._createBlocks( + clipBlockInfo.children, + block?.id + ); + await Promise.all(children.map(child => block?.append(child))); return block; }) ); From 8ccc997062b582d0684b620ff95ba52970812417 Mon Sep 17 00:00:00 2001 From: QiShaoXuan Date: Wed, 17 Aug 2022 19:47:54 +0800 Subject: [PATCH 3/9] refactor: refactor clip board --- .../src/editor/clipboard/browser-clipboard.ts | 12 -- .../editor/clipboard/clipboard-populator.ts | 132 +---------------- .../editor-core/src/editor/clipboard/copy.ts | 136 ++++++++++++++++++ 3 files changed, 142 insertions(+), 138 deletions(-) create mode 100644 libs/components/editor-core/src/editor/clipboard/copy.ts diff --git a/libs/components/editor-core/src/editor/clipboard/browser-clipboard.ts b/libs/components/editor-core/src/editor/clipboard/browser-clipboard.ts index 64eecbc5ca..687c5280ee 100644 --- a/libs/components/editor-core/src/editor/clipboard/browser-clipboard.ts +++ b/libs/components/editor-core/src/editor/clipboard/browser-clipboard.ts @@ -1,18 +1,6 @@ import { HooksRunner } from '../types'; -import { - OFFICE_CLIPBOARD_MIMETYPE, - InnerClipInfo, - ClipBlockInfo, -} from './types'; import { Editor } from '../editor'; -import { AsyncBlock } from '../block'; import ClipboardParse from './clipboard-parse'; -import { SelectInfo } from '../selection'; -import { - Protocol, - BlockFlavorKeys, - services, -} from '@toeverything/datasource/db-service'; import { MarkdownParser } from './markdown-parse'; import { shouldHandlerContinue } from './utils'; import { Paste } from './paste'; diff --git a/libs/components/editor-core/src/editor/clipboard/clipboard-populator.ts b/libs/components/editor-core/src/editor/clipboard/clipboard-populator.ts index 2f05866ab9..d7a8172566 100644 --- a/libs/components/editor-core/src/editor/clipboard/clipboard-populator.ts +++ b/libs/components/editor-core/src/editor/clipboard/clipboard-populator.ts @@ -1,23 +1,16 @@ import { Editor } from '../editor'; -import { SelectionManager, SelectInfo, SelectBlock } from '../selection'; +import { SelectionManager } from '../selection'; import { HookType, PluginHooks } from '../types'; -import { - ClipBlockInfo, - OFFICE_CLIPBOARD_MIMETYPE, - InnerClipInfo, -} from './types'; -import { Clip } from './clip'; -import assert from 'assert'; import ClipboardParse from './clipboard-parse'; import { Subscription } from 'rxjs'; - +import { Copy } from './copy'; class ClipboardPopulator { private _editor: Editor; private _hooks: PluginHooks; private _selectionManager: SelectionManager; private _clipboardParse: ClipboardParse; private _sub = new Subscription(); - + private _copy: Copy; constructor( editor: Editor, hooks: PluginHooks, @@ -27,128 +20,15 @@ class ClipboardPopulator { this._hooks = hooks; this._selectionManager = selectionManager; this._clipboardParse = new ClipboardParse(editor); + this._copy = new Copy(editor); this._sub.add( - hooks - .get(HookType.BEFORE_COPY) - .subscribe(this._populateAppClipboard) + hooks.get(HookType.BEFORE_COPY).subscribe(this._copy.handleCopy) ); this._sub.add( - hooks.get(HookType.BEFORE_CUT).subscribe(this._populateAppClipboard) + hooks.get(HookType.BEFORE_CUT).subscribe(this._copy.handleCopy) ); } - private _populateAppClipboard = async (e: ClipboardEvent) => { - e.preventDefault(); - e.stopPropagation(); - const clips = await this.getClips(); - if (!clips.length) { - return; - } - - // TODO: is not compatible with safari - const success = this._copyToClipboardFromPc(clips); - if (!success) { - // This way, not compatible with firefox - const clipboardData = e.clipboardData; - if (clipboardData) { - try { - clips.forEach(clip => { - clipboardData.setData( - clip.getMimeType(), - clip.getData() - ); - }); - } catch (e) { - // TODO handle exception - } - } - } - }; - - private _copyToClipboardFromPc(clips: any[]) { - let success = false; - const tempElem = document.createElement('textarea'); - tempElem.value = 'temp'; - document.body.appendChild(tempElem); - tempElem.select(); - tempElem.setSelectionRange(0, tempElem.value.length); - - const listener = function (e: any) { - const clipboardData = e.clipboardData; - if (clipboardData) { - clips.forEach(clip => { - clipboardData.setData(clip.getMimeType(), clip.getData()); - }); - } - - e.preventDefault(); - e.stopPropagation(); - tempElem.removeEventListener('copy', listener); - } as any; - - tempElem.addEventListener('copy', listener); - try { - success = document.execCommand('copy'); - } finally { - tempElem.removeEventListener('copy', listener); - document.body.removeChild(tempElem); - } - return success; - } - - private async _getClipBlockInfo(selBlock: SelectBlock) { - const block = await this._editor.getBlockById(selBlock.blockId); - const blockView = this._editor.getView(block.type); - assert(blockView); - const blockInfo: ClipBlockInfo = { - type: block.type, - properties: blockView.getSelProperties(block, selBlock), - children: [] as any[], - }; - - for (let i = 0; i < selBlock.children.length; i++) { - const childInfo = await this._getClipBlockInfo( - selBlock.children[i] - ); - blockInfo.children.push(childInfo); - } - - return blockInfo; - } - - private async _getInnerClip(): Promise { - const clips: ClipBlockInfo[] = []; - const selectInfo: SelectInfo = - await this._selectionManager.getSelectInfo(); - for (let i = 0; i < selectInfo.blocks.length; i++) { - const selBlock = selectInfo.blocks[i]; - const clipBlockInfo = await this._getClipBlockInfo(selBlock); - clips.push(clipBlockInfo); - } - return { - select: selectInfo, - data: clips, - }; - } - - async getClips() { - const clips: any[] = []; - - const innerClip = await this._getInnerClip(); - clips.push( - new Clip( - OFFICE_CLIPBOARD_MIMETYPE.DOCS_DOCUMENT_SLICE_CLIP_WRAPPED, - JSON.stringify(innerClip) - ) - ); - - const htmlClip = await this._clipboardParse.generateHtml(); - htmlClip && - clips.push(new Clip(OFFICE_CLIPBOARD_MIMETYPE.HTML, htmlClip)); - - return clips; - } - disposeInternal() { this._sub.unsubscribe(); this._hooks = null; diff --git a/libs/components/editor-core/src/editor/clipboard/copy.ts b/libs/components/editor-core/src/editor/clipboard/copy.ts new file mode 100644 index 0000000000..54ee46b26e --- /dev/null +++ b/libs/components/editor-core/src/editor/clipboard/copy.ts @@ -0,0 +1,136 @@ +import { Editor } from '../editor'; +import { SelectInfo, SelectBlock } from '../selection'; +import { + ClipBlockInfo, + OFFICE_CLIPBOARD_MIMETYPE, + InnerClipInfo, +} from './types'; +import { Clip } from './clip'; +import assert from 'assert'; +import ClipboardParse from './clipboard-parse'; + +class Copy { + private _editor: Editor; + private _clipboardParse: ClipboardParse; + + constructor(editor: Editor) { + this._editor = editor; + this._clipboardParse = new ClipboardParse(editor); + + this.handleCopy = this.handleCopy.bind(this); + } + public async handleCopy(e: ClipboardEvent) { + e.preventDefault(); + e.stopPropagation(); + const clips = await this.getClips(); + if (!clips.length) { + return; + } + // TODO: is not compatible with safari + const success = this._copyToClipboardFromPc(clips); + if (!success) { + // This way, not compatible with firefox + const clipboardData = e.clipboardData; + if (clipboardData) { + try { + clips.forEach(clip => { + clipboardData.setData( + clip.getMimeType(), + clip.getData() + ); + }); + } catch (e) { + // TODO handle exception + } + } + } + } + + async getClips() { + const clips: any[] = []; + + // get custom clip + const affineClip = await this._getAffineClip(); + clips.push( + new Clip( + OFFICE_CLIPBOARD_MIMETYPE.DOCS_DOCUMENT_SLICE_CLIP_WRAPPED, + JSON.stringify(affineClip) + ) + ); + + // get common html clip + const htmlClip = await this._clipboardParse.generateHtml(); + htmlClip && + clips.push(new Clip(OFFICE_CLIPBOARD_MIMETYPE.HTML, htmlClip)); + + return clips; + } + + private async _getAffineClip(): Promise { + const clips: ClipBlockInfo[] = []; + const selectInfo: SelectInfo = + await this._editor.selectionManager.getSelectInfo(); + for (let i = 0; i < selectInfo.blocks.length; i++) { + const selBlock = selectInfo.blocks[i]; + const clipBlockInfo = await this._getClipInfoOfBlock(selBlock); + clips.push(clipBlockInfo); + } + return { + select: selectInfo, + data: clips, + }; + } + + private async _getClipInfoOfBlock(selBlock: SelectBlock) { + const block = await this._editor.getBlockById(selBlock.blockId); + const blockView = this._editor.getView(block.type); + assert(blockView); + const blockInfo: ClipBlockInfo = { + type: block.type, + properties: blockView.getSelProperties(block, selBlock), + children: [] as any[], + }; + + for (let i = 0; i < selBlock.children.length; i++) { + const childInfo = await this._getClipInfoOfBlock( + selBlock.children[i] + ); + blockInfo.children.push(childInfo); + } + + return blockInfo; + } + + private _copyToClipboardFromPc(clips: any[]) { + let success = false; + const tempElem = document.createElement('textarea'); + tempElem.value = 'temp'; + document.body.appendChild(tempElem); + tempElem.select(); + tempElem.setSelectionRange(0, tempElem.value.length); + + const listener = function (e: any) { + const clipboardData = e.clipboardData; + if (clipboardData) { + clips.forEach(clip => { + clipboardData.setData(clip.getMimeType(), clip.getData()); + }); + } + + e.preventDefault(); + e.stopPropagation(); + tempElem.removeEventListener('copy', listener); + } as any; + + tempElem.addEventListener('copy', listener); + try { + success = document.execCommand('copy'); + } finally { + tempElem.removeEventListener('copy', listener); + document.body.removeChild(tempElem); + } + return success; + } +} + +export { Copy }; From 3c8b04d91a05a96974447f9a0cfeebb0d3ad9a1f Mon Sep 17 00:00:00 2001 From: QiShaoXuan Date: Wed, 17 Aug 2022 23:28:18 +0800 Subject: [PATCH 4/9] fix: can not copy from white board and paste in editor --- libs/components/affine-board/src/Board.tsx | 25 +++++++++++-- libs/components/board-state/src/tldraw-app.ts | 35 ++++++++++++------ .../editor-core/src/editor/clipboard/utils.ts | 37 +++++++++++++++++++ .../editor-core/src/editor/index.ts | 1 + 4 files changed, 84 insertions(+), 14 deletions(-) diff --git a/libs/components/affine-board/src/Board.tsx b/libs/components/affine-board/src/Board.tsx index 82c2948c49..6192ac3d74 100644 --- a/libs/components/affine-board/src/Board.tsx +++ b/libs/components/affine-board/src/Board.tsx @@ -5,7 +5,10 @@ import { getSession } from '@toeverything/components/board-sessions'; import { deepCopy, TldrawApp } from '@toeverything/components/board-state'; import { tools } from '@toeverything/components/board-tools'; import { TDShapeType } from '@toeverything/components/board-types'; -import { RecastBlockProvider } from '@toeverything/components/editor-core'; +import { + RecastBlockProvider, + getClipDataOfBlocksById, +} from '@toeverything/components/editor-core'; import { services } from '@toeverything/datasource/db-service'; import { AsyncBlock, BlockEditor } from '@toeverything/framework/virgo'; import { useEffect, useState } from 'react'; @@ -16,7 +19,11 @@ interface AffineBoardProps { rootBlockId: string; } -const AffineBoard = ({ workspace, rootBlockId }: AffineBoardProps) => { +const AffineBoard = ({ + workspace, + rootBlockId, + editor, +}: AffineBoardProps & { editor: BlockEditor }) => { const [app, set_app] = useState(); const [document] = useState(() => { @@ -62,6 +69,14 @@ const AffineBoard = ({ workspace, rootBlockId }: AffineBoardProps) => { onMount(app) { set_app(app); }, + async onCopy(e, groupIds) { + const [mimeType, data] = await getClipDataOfBlocksById( + editor, + groupIds + ); + + e.clipboardData?.setData(mimeType, data); + }, onChangePage(app, shapes, bindings, assets) { Promise.all( Object.entries(shapes).map(async ([id, shape]) => { @@ -130,7 +145,11 @@ export const AffineBoardWitchContext = ({ }, [editor, rootBlockId]); return page ? ( - + ) : null; }; diff --git a/libs/components/board-state/src/tldraw-app.ts b/libs/components/board-state/src/tldraw-app.ts index 16d6edc1eb..2a4ba54433 100644 --- a/libs/components/board-state/src/tldraw-app.ts +++ b/libs/components/board-state/src/tldraw-app.ts @@ -171,6 +171,10 @@ interface TDCallbacks { * (optional) A callback to run when the user exports their page or selection. */ onExport?: (app: TldrawApp, info: TDExport) => Promise; + /** + * (optional) A callback to run when the shape is copied. + */ + onCopy?: (e: ClipboardEvent, ids: string[]) => void; } export interface TldrawAppCtorProps { @@ -1898,12 +1902,14 @@ export class TldrawApp extends StateManager { /** * Copy one or more shapes to the clipboard. * @param ids The ids of the shapes to copy. + * @param pageId + * @param e */ - copy = ( + copy = async ( ids = this.selectedIds, pageId = this.currentPageId, e?: ClipboardEvent - ): this => { + ) => { e?.preventDefault(); this.clipboard = this.get_clipboard(ids, pageId); @@ -1919,17 +1925,24 @@ export class TldrawApp extends StateManager { if (e) { e.clipboardData?.setData('text/html', tldrawString); + await this.callbacks.onCopy?.(e, this.selectedIds); } - if (navigator.clipboard && window.ClipboardItem) { - navigator.clipboard.write([ - new ClipboardItem({ - 'text/html': new Blob([tldrawString], { - type: 'text/html', - }), - }), - ]); - } + /** + * Reasons for not using Clipboard API for now: + * 1. The `clipboardData.setData` method temporarily satisfies the need for replication functionality + * 2. Clipboard API requires the user to agree to access(https://developer.mozilla.org/en-US/docs/Web/API/Permissions_API) + * + * **/ + // if (navigator.clipboard && window.ClipboardItem) { + // navigator.clipboard.write([ + // new ClipboardItem({ + // 'text/html': new Blob([tldrawString], { + // type: 'text/html', + // }), + // }), + // ]); + // } this.pasteInfo.offset = [0, 0]; this.pasteInfo.center = [0, 0]; diff --git a/libs/components/editor-core/src/editor/clipboard/utils.ts b/libs/components/editor-core/src/editor/clipboard/utils.ts index cb5d10241e..465d2788b8 100644 --- a/libs/components/editor-core/src/editor/clipboard/utils.ts +++ b/libs/components/editor-core/src/editor/clipboard/utils.ts @@ -1,4 +1,6 @@ import { Editor } from '../editor'; +import { ClipBlockInfo, OFFICE_CLIPBOARD_MIMETYPE } from './types'; +import { Clip } from './clip'; export const shouldHandlerContinue = (event: Event, editor: Editor) => { const filterNodes = ['INPUT', 'SELECT', 'TEXTAREA']; @@ -12,3 +14,38 @@ export const shouldHandlerContinue = (event: Event, editor: Editor) => { return editor.selectionManager.currentSelectInfo.type !== 'None'; }; + +export const getClipInfoOfBlockById = async ( + editor: Editor, + blockId: string +) => { + const block = await editor.getBlockById(blockId); + const blockView = editor.getView(block.type); + const blockInfo: ClipBlockInfo = { + type: block.type, + properties: blockView.getSelProperties(block, {}), + children: [] as ClipBlockInfo[], + }; + const children = await block?.children(); + + for (let i = 0; i < children.length; i++) { + const childInfo = await getClipInfoOfBlockById(editor, children[i].id); + blockInfo.children.push(childInfo); + } + return blockInfo; +}; + +export const getClipDataOfBlocksById = async ( + editor: Editor, + blockIds: string[] +) => { + const clipInfos = await Promise.all( + blockIds.map(blockId => getClipInfoOfBlockById(editor, blockId)) + ); + return [ + OFFICE_CLIPBOARD_MIMETYPE.DOCS_DOCUMENT_SLICE_CLIP_WRAPPED, + JSON.stringify({ + data: clipInfos, + }), + ]; +}; diff --git a/libs/components/editor-core/src/editor/index.ts b/libs/components/editor-core/src/editor/index.ts index 03903d31bf..3131e6f0a1 100644 --- a/libs/components/editor-core/src/editor/index.ts +++ b/libs/components/editor-core/src/editor/index.ts @@ -10,3 +10,4 @@ export { BlockDropPlacement, HookType, GroupDirection } from './types'; export type { Plugin, PluginCreator, PluginHooks, Virgo } from './types'; export { BaseView, getTextHtml, getTextProperties } from './views/base-view'; export type { ChildrenView, CreateView } from './views/base-view'; +export { getClipDataOfBlocksById } from './clipboard/utils'; From 16a6cca015dfc1b8ecbd173ddd5183c1d62772c2 Mon Sep 17 00:00:00 2001 From: QiShaoXuan Date: Thu, 18 Aug 2022 13:57:58 +0800 Subject: [PATCH 5/9] refactor: code of copy --- libs/components/affine-board/src/Board.tsx | 7 ++- .../editor-core/src/editor/clipboard/copy.ts | 54 ++++--------------- .../editor-core/src/editor/clipboard/utils.ts | 7 +-- 3 files changed, 19 insertions(+), 49 deletions(-) diff --git a/libs/components/affine-board/src/Board.tsx b/libs/components/affine-board/src/Board.tsx index 6192ac3d74..4e99d6bbb0 100644 --- a/libs/components/affine-board/src/Board.tsx +++ b/libs/components/affine-board/src/Board.tsx @@ -70,12 +70,15 @@ const AffineBoard = ({ set_app(app); }, async onCopy(e, groupIds) { - const [mimeType, data] = await getClipDataOfBlocksById( + const clip = await getClipDataOfBlocksById( editor, groupIds ); - e.clipboardData?.setData(mimeType, data); + e.clipboardData?.setData( + clip.getMimeType(), + clip.getData() + ); }, onChangePage(app, shapes, bindings, assets) { Promise.all( diff --git a/libs/components/editor-core/src/editor/clipboard/copy.ts b/libs/components/editor-core/src/editor/clipboard/copy.ts index 54ee46b26e..f34269f57f 100644 --- a/libs/components/editor-core/src/editor/clipboard/copy.ts +++ b/libs/components/editor-core/src/editor/clipboard/copy.ts @@ -1,13 +1,9 @@ import { Editor } from '../editor'; -import { SelectInfo, SelectBlock } from '../selection'; -import { - ClipBlockInfo, - OFFICE_CLIPBOARD_MIMETYPE, - InnerClipInfo, -} from './types'; +import { SelectInfo } from '../selection'; +import { OFFICE_CLIPBOARD_MIMETYPE } from './types'; import { Clip } from './clip'; -import assert from 'assert'; import ClipboardParse from './clipboard-parse'; +import { getClipDataOfBlocksById } from './utils'; class Copy { private _editor: Editor; @@ -47,16 +43,11 @@ class Copy { } async getClips() { - const clips: any[] = []; + const clips: Clip[] = []; // get custom clip const affineClip = await this._getAffineClip(); - clips.push( - new Clip( - OFFICE_CLIPBOARD_MIMETYPE.DOCS_DOCUMENT_SLICE_CLIP_WRAPPED, - JSON.stringify(affineClip) - ) - ); + clips.push(affineClip); // get common html clip const htmlClip = await this._clipboardParse.generateHtml(); @@ -66,39 +57,14 @@ class Copy { return clips; } - private async _getAffineClip(): Promise { - const clips: ClipBlockInfo[] = []; + private async _getAffineClip(): Promise { const selectInfo: SelectInfo = await this._editor.selectionManager.getSelectInfo(); - for (let i = 0; i < selectInfo.blocks.length; i++) { - const selBlock = selectInfo.blocks[i]; - const clipBlockInfo = await this._getClipInfoOfBlock(selBlock); - clips.push(clipBlockInfo); - } - return { - select: selectInfo, - data: clips, - }; - } - private async _getClipInfoOfBlock(selBlock: SelectBlock) { - const block = await this._editor.getBlockById(selBlock.blockId); - const blockView = this._editor.getView(block.type); - assert(blockView); - const blockInfo: ClipBlockInfo = { - type: block.type, - properties: blockView.getSelProperties(block, selBlock), - children: [] as any[], - }; - - for (let i = 0; i < selBlock.children.length; i++) { - const childInfo = await this._getClipInfoOfBlock( - selBlock.children[i] - ); - blockInfo.children.push(childInfo); - } - - return blockInfo; + return getClipDataOfBlocksById( + this._editor, + selectInfo.blocks.map(block => block.blockId) + ); } private _copyToClipboardFromPc(clips: any[]) { diff --git a/libs/components/editor-core/src/editor/clipboard/utils.ts b/libs/components/editor-core/src/editor/clipboard/utils.ts index 465d2788b8..f9ae879b5c 100644 --- a/libs/components/editor-core/src/editor/clipboard/utils.ts +++ b/libs/components/editor-core/src/editor/clipboard/utils.ts @@ -42,10 +42,11 @@ export const getClipDataOfBlocksById = async ( const clipInfos = await Promise.all( blockIds.map(blockId => getClipInfoOfBlockById(editor, blockId)) ); - return [ + + return new Clip( OFFICE_CLIPBOARD_MIMETYPE.DOCS_DOCUMENT_SLICE_CLIP_WRAPPED, JSON.stringify({ data: clipInfos, - }), - ]; + }) + ); }; From fa1d011135f106fce5f239ebb7e8cc8108ce2ca5 Mon Sep 17 00:00:00 2001 From: QiShaoXuan Date: Thu, 18 Aug 2022 17:30:09 +0800 Subject: [PATCH 6/9] fix: wrong block order when pasting multiple blocks --- .../editor-core/src/editor/clipboard/paste.ts | 57 +++++++++++-------- 1 file changed, 32 insertions(+), 25 deletions(-) diff --git a/libs/components/editor-core/src/editor/clipboard/paste.ts b/libs/components/editor-core/src/editor/clipboard/paste.ts index 8b5ed40ee5..5e4021e978 100644 --- a/libs/components/editor-core/src/editor/clipboard/paste.ts +++ b/libs/components/editor-core/src/editor/clipboard/paste.ts @@ -241,15 +241,12 @@ export class Paste { [] ); - selectedBlock.setProperties({ + await selectedBlock.setProperties({ text: { value: newTextValue, }, }); - const pasteBlocks = await this._createBlocks(blocks); - await Promise.all( - pasteBlocks.map(block => selectedBlock.after(block)) - ); + const pastedBlocks = await this._createBlocks(blocks); const nextBlock = await this._editor.createBlock( selectedBlock?.type @@ -259,11 +256,12 @@ export class Paste { value: nextTextValue, }, }); - pasteBlocks[pasteBlocks.length - 1].after(nextBlock); - this._setEndSelectToBlock( - pasteBlocks[pasteBlocks.length - 1].id - ); + await this._insertBlocksAfterBlock(selectedBlock, [ + ...pastedBlocks, + nextBlock, + ]); + await this._setEndSelectToBlock(nextBlock.id); } else { this._editor.blockHelper.insertNodes( selectedBlock.id, @@ -327,10 +325,7 @@ export class Paste { value: newTextValue, }, }); - const pasteBlocks = await this._createBlocks(blocks); - pasteBlocks.forEach((block: AsyncBlock) => { - selectedBlock.after(block); - }); + const pastedBlocks = await this._createBlocks(blocks); const nextBlock = await this._editor.createBlock( selectedBlock?.type ); @@ -339,11 +334,12 @@ export class Paste { value: nextTextValue, }, }); - pasteBlocks[pasteBlocks.length - 1].after(nextBlock); + await this._insertBlocksAfterBlock(selectedBlock, [ + ...pastedBlocks, + nextBlock, + ]); - this._setEndSelectToBlock( - pasteBlocks[pasteBlocks.length - 1].id - ); + await this._setEndSelectToBlock(nextBlock.id); } else { this._editor.blockHelper.insertNodes( selectedBlock.id, @@ -353,10 +349,10 @@ export class Paste { } } } else { - const pasteBlocks = await this._createBlocks(blocks); + const pastedBlocks = await this._createBlocks(blocks); await Promise.all( - pasteBlocks.map(block => selectedBlock.after(block)) + pastedBlocks.map(block => selectedBlock.after(block)) ); if (isSelectedBlockEmpty) { @@ -364,7 +360,7 @@ export class Paste { } this._setEndSelectToBlock( - pasteBlocks[pasteBlocks.length - 1].id + pastedBlocks[pastedBlocks.length - 1].id ); } } @@ -374,28 +370,39 @@ export class Paste { currentSelectInfo.blocks[currentSelectInfo.blocks.length - 1] .blockId ); - const pasteBlocks = await this._createBlocks(blocks); + const pastedBlocks = await this._createBlocks(blocks); let groupBlock: AsyncBlock; if (selectedBlock?.type === 'page') { groupBlock = await this._editor.createBlock('group'); await Promise.all( - pasteBlocks.map(block => groupBlock.append(block)) + pastedBlocks.map(block => groupBlock.append(block)) ); await selectedBlock.after(groupBlock); } else if (selectedBlock?.type === 'group') { await Promise.all( - pasteBlocks.map(block => selectedBlock.append(block)) + pastedBlocks.map(block => selectedBlock.append(block)) ); } else { await Promise.all( - pasteBlocks.map(block => selectedBlock.after(block)) + pastedBlocks.map(block => selectedBlock.after(block)) ); } - this._setEndSelectToBlock(pasteBlocks[pasteBlocks.length - 1].id); + this._setEndSelectToBlock(pastedBlocks[pastedBlocks.length - 1].id); } } + private async _insertBlocksAfterBlock( + targetBlock: AsyncBlock, + blocks: AsyncBlock[] + ) { + if (blocks.length === 0) { + return; + } + const [firstBlock, ...otherBlock] = blocks; + await targetBlock.after(firstBlock); + await this._insertBlocksAfterBlock(blocks[0], otherBlock); + } private async _setEndSelectToBlock(blockId: string) { const block = await this._editor.getBlockById(blockId); const isBlockCanEdit = Paste._isTextEditBlock(block.type); From d1835e8cad65d56672d0fff44c1ded90e91a16f5 Mon Sep 17 00:00:00 2001 From: QiShaoXuan Date: Thu, 18 Aug 2022 18:20:07 +0800 Subject: [PATCH 7/9] feat: update logic of copy group block --- .../editor-core/src/editor/clipboard/paste.ts | 27 ++++++++++++++++--- 1 file changed, 23 insertions(+), 4 deletions(-) diff --git a/libs/components/editor-core/src/editor/clipboard/paste.ts b/libs/components/editor-core/src/editor/clipboard/paste.ts index 5e4021e978..37168c2614 100644 --- a/libs/components/editor-core/src/editor/clipboard/paste.ts +++ b/libs/components/editor-core/src/editor/clipboard/paste.ts @@ -414,16 +414,35 @@ export class Paste { }, 100); } - private async _createBlocks(blocks: ClipBlockInfo[], parentId?: string) { + private _flatGroupBlocks(blocks: ClipBlockInfo[]) { + return blocks.reduce( + (blockList: ClipBlockInfo[], block: ClipBlockInfo) => { + if (block.type === 'group') { + block?.children?.forEach(childBlock => { + childBlock.children = this._flatGroupBlocks( + childBlock.children + ); + }); + block?.children?.length && + blockList.push(...block.children); + } else { + blockList.push(block); + block.children = this._flatGroupBlocks(block.children); + } + return blockList; + }, + [] + ); + } + private async _createBlocks(blocks: ClipBlockInfo[]) { return Promise.all( - blocks.map(async clipBlockInfo => { + this._flatGroupBlocks(blocks).map(async clipBlockInfo => { const block = await this._editor.createBlock( clipBlockInfo.type ); block?.setProperties(clipBlockInfo.properties); const children = await this._createBlocks( - clipBlockInfo.children, - block?.id + clipBlockInfo.children ); await Promise.all(children.map(child => block?.append(child))); return block; From 61f15ba843ab3383a4e1667dcc4a30f343a0f8d0 Mon Sep 17 00:00:00 2001 From: Qi <474021214@qq.com> Date: Thu, 18 Aug 2022 20:04:03 +0800 Subject: [PATCH 8/9] Update libs/components/editor-core/src/editor/clipboard/utils.ts Co-authored-by: Whitewater --- libs/components/editor-core/src/editor/clipboard/utils.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libs/components/editor-core/src/editor/clipboard/utils.ts b/libs/components/editor-core/src/editor/clipboard/utils.ts index f9ae879b5c..aedc28c22c 100644 --- a/libs/components/editor-core/src/editor/clipboard/utils.ts +++ b/libs/components/editor-core/src/editor/clipboard/utils.ts @@ -26,7 +26,7 @@ export const getClipInfoOfBlockById = async ( properties: blockView.getSelProperties(block, {}), children: [] as ClipBlockInfo[], }; - const children = await block?.children(); + const children = await block?.children() ?? []; for (let i = 0; i < children.length; i++) { const childInfo = await getClipInfoOfBlockById(editor, children[i].id); From 66a36481e1af3cb736ca8ef0fe441537b3c08f8f Mon Sep 17 00:00:00 2001 From: QiShaoXuan Date: Thu, 18 Aug 2022 20:09:35 +0800 Subject: [PATCH 9/9] fix: prettier code style --- libs/components/editor-core/src/editor/clipboard/copy.ts | 2 +- libs/components/editor-core/src/editor/clipboard/utils.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/libs/components/editor-core/src/editor/clipboard/copy.ts b/libs/components/editor-core/src/editor/clipboard/copy.ts index f34269f57f..94b3117264 100644 --- a/libs/components/editor-core/src/editor/clipboard/copy.ts +++ b/libs/components/editor-core/src/editor/clipboard/copy.ts @@ -4,7 +4,7 @@ import { OFFICE_CLIPBOARD_MIMETYPE } from './types'; import { Clip } from './clip'; import ClipboardParse from './clipboard-parse'; import { getClipDataOfBlocksById } from './utils'; - +import { copyToClipboard } from '@toeverything/utils'; class Copy { private _editor: Editor; private _clipboardParse: ClipboardParse; diff --git a/libs/components/editor-core/src/editor/clipboard/utils.ts b/libs/components/editor-core/src/editor/clipboard/utils.ts index aedc28c22c..c807a869a0 100644 --- a/libs/components/editor-core/src/editor/clipboard/utils.ts +++ b/libs/components/editor-core/src/editor/clipboard/utils.ts @@ -26,7 +26,7 @@ export const getClipInfoOfBlockById = async ( properties: blockView.getSelProperties(block, {}), children: [] as ClipBlockInfo[], }; - const children = await block?.children() ?? []; + const children = (await block?.children()) ?? []; for (let i = 0; i < children.length; i++) { const childInfo = await getClipInfoOfBlockById(editor, children[i].id);