From aa877b64911fb2dbe0d85c3ae20470fa6842799e Mon Sep 17 00:00:00 2001 From: QiShaoXuan Date: Tue, 16 Aug 2022 17:28:40 +0800 Subject: [PATCH 01/24] 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 02/24] 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 03/24] 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 04/24] 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 5959c088737fd521fba71b9cb2a32a50abc27975 Mon Sep 17 00:00:00 2001 From: alt0 Date: Thu, 18 Aug 2022 12:16:12 +0800 Subject: [PATCH 05/24] fix: remove asyncBlock cache --- libs/components/editor-core/src/editor/editor.ts | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/libs/components/editor-core/src/editor/editor.ts b/libs/components/editor-core/src/editor/editor.ts index 1d1cc95684..2cd379bb26 100644 --- a/libs/components/editor-core/src/editor/editor.ts +++ b/libs/components/editor-core/src/editor/editor.ts @@ -1,6 +1,5 @@ /* eslint-disable max-lines */ import HotKeys from 'hotkeys-js'; -import LRUCache from 'lru-cache'; import type { PatchNode } from '@toeverything/components/ui'; import type { @@ -46,10 +45,7 @@ export interface EditorCtorProps { } export class Editor implements Virgo { - private _cacheManager = new LRUCache>({ - max: 8192, - ttl: 1000 * 60 * 30, - }); + private _cacheManager = new Map>(); public mouseManager = new MouseManager(this); public selectionManager = new SelectionManager(this); public keyboardManager = new KeyboardManager(this); From 16a6cca015dfc1b8ecbd173ddd5183c1d62772c2 Mon Sep 17 00:00:00 2001 From: QiShaoXuan Date: Thu, 18 Aug 2022 13:57:58 +0800 Subject: [PATCH 06/24] 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 68cadca5ca84ad5d7d0b5b90c26aa1508ea8ccf7 Mon Sep 17 00:00:00 2001 From: alt0 Date: Thu, 18 Aug 2022 14:29:44 +0800 Subject: [PATCH 07/24] fix: if delete block, clear cache --- libs/components/editor-core/src/editor/editor.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/libs/components/editor-core/src/editor/editor.ts b/libs/components/editor-core/src/editor/editor.ts index 2cd379bb26..e85db2ba65 100644 --- a/libs/components/editor-core/src/editor/editor.ts +++ b/libs/components/editor-core/src/editor/editor.ts @@ -230,7 +230,12 @@ export class Editor implements Virgo { return await services.api.editorBlock.update(patches); }, remove: async ({ workspace, id }: WorkspaceAndBlockId) => { - return await services.api.editorBlock.delete({ workspace, id }); + const ret = await services.api.editorBlock.delete({ + workspace, + id, + }); + this._cacheManager.delete(id); + return ret; }, observe: async ( { workspace, id }: WorkspaceAndBlockId, From 41bb7368ffe7b8f7089962ac3179eef0495afbb9 Mon Sep 17 00:00:00 2001 From: alt0 Date: Thu, 18 Aug 2022 15:45:58 +0800 Subject: [PATCH 08/24] fix: autofocus when create group in board --- .../board-shapes/src/editor-util/EditorUtil.tsx | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/libs/components/board-shapes/src/editor-util/EditorUtil.tsx b/libs/components/board-shapes/src/editor-util/EditorUtil.tsx index 6e6f123a72..0855310081 100644 --- a/libs/components/board-shapes/src/editor-util/EditorUtil.tsx +++ b/libs/components/board-shapes/src/editor-util/EditorUtil.tsx @@ -8,6 +8,7 @@ import { TDShapeType, TransformInfo, } from '@toeverything/components/board-types'; +import type { BlockEditor } from '@toeverything/components/editor-core'; import { MIN_PAGE_WIDTH } from '@toeverything/components/editor-core'; import { styled } from '@toeverything/components/ui'; import type { SyntheticEvent } from 'react'; @@ -66,6 +67,7 @@ export class EditorUtil extends TDShapeUtil { Component = TDShapeUtil.Component( ({ shape, meta: { app }, events, isEditing, onShapeChange }, ref) => { const containerRef = useRef(); + const editorRef = useRef(); const { workspace, rootBlockId, @@ -135,6 +137,18 @@ export class EditorUtil extends TDShapeUtil { } }, [app, state, shape.id, editingText, editingId]); + useEffect(() => { + (async () => { + if (isEditing) { + const lastBlock = + await editorRef.current.getLastBlock(); + editorRef.current.selectionManager.activeNodeByNodeId( + lastBlock.id + ); + } + })(); + }, [isEditing]); + return ( { rootBlockId={rootBlockId} scrollBlank={false} isEdgeless + ref={editorRef} /> {editingText ? null : } From a5289844790a002de4c9a374ff75800540570649 Mon Sep 17 00:00:00 2001 From: austaras Date: Wed, 17 Aug 2022 18:56:18 +0800 Subject: [PATCH 09/24] feat(whiteboard): activate text on dblclick --- .../board-shapes/src/editor-util/EditorUtil.tsx | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/libs/components/board-shapes/src/editor-util/EditorUtil.tsx b/libs/components/board-shapes/src/editor-util/EditorUtil.tsx index 6e6f123a72..cb6e56275a 100644 --- a/libs/components/board-shapes/src/editor-util/EditorUtil.tsx +++ b/libs/components/board-shapes/src/editor-util/EditorUtil.tsx @@ -10,7 +10,7 @@ import { } from '@toeverything/components/board-types'; import { MIN_PAGE_WIDTH } from '@toeverything/components/editor-core'; import { styled } from '@toeverything/components/ui'; -import type { SyntheticEvent } from 'react'; +import type { MouseEvent, SyntheticEvent } from 'react'; import { memo, useCallback, useEffect, useRef } from 'react'; import { defaultTextStyle, @@ -135,6 +135,15 @@ export class EditorUtil extends TDShapeUtil { } }, [app, state, shape.id, editingText, editingId]); + const onMouseDown = useCallback( + (e: MouseEvent) => { + if (e.detail === 2) { + app.setEditingText(shape.id); + } + }, + [app, shape.id] + ); + return ( { onPointerDown={stopPropagation} onMouseEnter={activateIfEditing} onDragEnter={activateIfEditing} + onMouseDown={onMouseDown} > Date: Thu, 18 Aug 2022 17:30:09 +0800 Subject: [PATCH 10/24] 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 6bdb7b48762f3f700bf3f330ffb93b9473143834 Mon Sep 17 00:00:00 2001 From: lawvs <18554747+lawvs@users.noreply.github.com> Date: Fri, 5 Aug 2022 18:55:30 +0800 Subject: [PATCH 11/24] chore: disable selection group plugin --- libs/components/editor-plugins/src/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libs/components/editor-plugins/src/index.ts b/libs/components/editor-plugins/src/index.ts index be7bb9bd97..ab657a9904 100644 --- a/libs/components/editor-plugins/src/index.ts +++ b/libs/components/editor-plugins/src/index.ts @@ -21,7 +21,7 @@ export const plugins: PluginCreator[] = [ CommandMenuPlugin, ReferenceMenuPlugin, TemplatePlugin, - SelectionGroupPlugin, + // SelectionGroupPlugin, AddCommentPlugin, GroupMenuPlugin, ]; From 407ee4d8f0ca544580e407e76007410f1ea82725 Mon Sep 17 00:00:00 2001 From: lawvs <18554747+lawvs@users.noreply.github.com> Date: Fri, 5 Aug 2022 18:56:25 +0800 Subject: [PATCH 12/24] feat: add kanban card mask --- .../blocks/group/scene-kanban/CardItem.tsx | 63 ++++++++++++++++--- 1 file changed, 53 insertions(+), 10 deletions(-) diff --git a/libs/components/editor-blocks/src/blocks/group/scene-kanban/CardItem.tsx b/libs/components/editor-blocks/src/blocks/group/scene-kanban/CardItem.tsx index 76220d7ff0..eba13f0db6 100644 --- a/libs/components/editor-blocks/src/blocks/group/scene-kanban/CardItem.tsx +++ b/libs/components/editor-blocks/src/blocks/group/scene-kanban/CardItem.tsx @@ -4,8 +4,14 @@ import { useKanban, useRefPage, } from '@toeverything/components/editor-core'; -import { styled } from '@toeverything/components/ui'; +import { PenIcon } from '@toeverything/components/icons'; +import { + IconButton, + MuiClickAwayListener, + styled, +} from '@toeverything/components/ui'; import { useFlag } from '@toeverything/datasource/feature-flags'; +import { useState } from 'react'; const CardContent = styled('div')({ margin: '20px', @@ -23,6 +29,7 @@ const CardActions = styled('div')({ fontWeight: '300', color: '#98ACBD', transition: 'all ease-in 0.2s', + zIndex: 1, ':hover': { background: '#F5F7F8', @@ -39,11 +46,13 @@ const PlusIcon = styled('div')({ }); const CardContainer = styled('div')({ + position: 'relative', display: 'flex', flexDirection: 'column', backgroundColor: '#fff', border: '1px solid #E2E7ED', borderRadius: '5px', + overflow: 'hidden', [CardActions.toString()]: { opacity: '0', @@ -55,6 +64,23 @@ const CardContainer = styled('div')({ }, }); +const Overlay = styled('div')({ + position: 'absolute', + width: '100%', + height: '100%', + background: 'transparent', + + '& > *': { + visibility: 'hidden', + position: 'absolute', + right: '24px', + top: '16px', + }, + '&:hover > *': { + visibility: 'visible', + }, +}); + export const CardItem = ({ id, block, @@ -64,8 +90,11 @@ export const CardItem = ({ }) => { const { addSubItem } = useKanban(); const { openSubPage } = useRefPage(); + const [editable, setEditable] = useState(false); const showKanbanRefPageFlag = useFlag('ShowKanbanRefPage', false); + const onAddItem = async () => { + setEditable(true); await addSubItem(block); }; @@ -74,14 +103,28 @@ export const CardItem = ({ }; return ( - - - - - - - Add a sub-block - - + setEditable(false)}> + + + + + {!editable && ( + + { + e.stopPropagation(); + setEditable(true); + }} + > + + + + )} + + + Add a sub-block + + + ); }; From dcdc7f8862e3b5435c1ebfd0d9c6688b0de1efa7 Mon Sep 17 00:00:00 2001 From: lawvs <18554747+lawvs@users.noreply.github.com> Date: Wed, 10 Aug 2022 17:41:13 +0800 Subject: [PATCH 13/24] refactor: ref page use AffineEditor --- .../src/blocks/group/scene-kanban/CardItem.tsx | 7 ++----- .../src/blocks/group/scene-kanban/RefPage.tsx} | 16 +++++++++++++--- libs/components/editor-core/src/index.ts | 2 +- .../editor-core/src/recast-block/Context.tsx | 3 +-- .../components/editor-core/src/ref-page/index.ts | 1 - 5 files changed, 17 insertions(+), 12 deletions(-) rename libs/components/{editor-core/src/ref-page/ModalPage.tsx => editor-blocks/src/blocks/group/scene-kanban/RefPage.tsx} (82%) delete mode 100644 libs/components/editor-core/src/ref-page/index.ts diff --git a/libs/components/editor-blocks/src/blocks/group/scene-kanban/CardItem.tsx b/libs/components/editor-blocks/src/blocks/group/scene-kanban/CardItem.tsx index eba13f0db6..c1cc9f9a1c 100644 --- a/libs/components/editor-blocks/src/blocks/group/scene-kanban/CardItem.tsx +++ b/libs/components/editor-blocks/src/blocks/group/scene-kanban/CardItem.tsx @@ -1,9 +1,5 @@ import type { KanbanCard } from '@toeverything/components/editor-core'; -import { - RenderBlock, - useKanban, - useRefPage, -} from '@toeverything/components/editor-core'; +import { RenderBlock, useKanban } from '@toeverything/components/editor-core'; import { PenIcon } from '@toeverything/components/icons'; import { IconButton, @@ -12,6 +8,7 @@ import { } from '@toeverything/components/ui'; import { useFlag } from '@toeverything/datasource/feature-flags'; import { useState } from 'react'; +import { useRefPage } from './RefPage'; const CardContent = styled('div')({ margin: '20px', diff --git a/libs/components/editor-core/src/ref-page/ModalPage.tsx b/libs/components/editor-blocks/src/blocks/group/scene-kanban/RefPage.tsx similarity index 82% rename from libs/components/editor-core/src/ref-page/ModalPage.tsx rename to libs/components/editor-blocks/src/blocks/group/scene-kanban/RefPage.tsx index 52e1d6554f..cd3588162e 100644 --- a/libs/components/editor-core/src/ref-page/ModalPage.tsx +++ b/libs/components/editor-blocks/src/blocks/group/scene-kanban/RefPage.tsx @@ -1,7 +1,8 @@ +import { AffineEditor } from '@toeverything/components/affine-editor'; +import { useEditor } from '@toeverything/components/editor-core'; import { MuiBackdrop, styled, useTheme } from '@toeverything/components/ui'; import { createContext, ReactNode, useContext, useState } from 'react'; import { createPortal } from 'react-dom'; -import { RenderBlock } from '../render-block'; const Dialog = styled('div')({ flex: 1, @@ -30,7 +31,7 @@ const Modal = ({ open, children }: { open: boolean; children?: ReactNode }) => { onClick={closeSubPage} > { + onClick={(e: { stopPropagation: () => void }) => { e.stopPropagation(); }} > @@ -43,9 +44,18 @@ const Modal = ({ open, children }: { open: boolean; children?: ReactNode }) => { }; const ModalPage = ({ blockId }: { blockId: string | null }) => { + const { editor } = useEditor(); + return ( - {blockId && } + {blockId && ( + + )} ); }; diff --git a/libs/components/editor-core/src/index.ts b/libs/components/editor-core/src/index.ts index bea2fed3a0..d28d8e96b3 100644 --- a/libs/components/editor-core/src/index.ts +++ b/libs/components/editor-core/src/index.ts @@ -16,4 +16,4 @@ export * from './utils'; export * from './editor'; -export { RefPageProvider, useRefPage } from './ref-page'; +export { useEditor } from './Contexts'; diff --git a/libs/components/editor-core/src/recast-block/Context.tsx b/libs/components/editor-core/src/recast-block/Context.tsx index 47ec6cfcdb..4236ede55a 100644 --- a/libs/components/editor-core/src/recast-block/Context.tsx +++ b/libs/components/editor-core/src/recast-block/Context.tsx @@ -2,7 +2,6 @@ import { Protocol } from '@toeverything/datasource/db-service'; import { AsyncBlock } from '../editor'; import { ComponentType, createContext, ReactNode, useContext } from 'react'; import { RecastBlock } from './types'; -import { RefPageProvider } from '../ref-page'; /** * Determine whether the block supports RecastBlock @@ -48,7 +47,7 @@ export const RecastBlockProvider = ({ return ( - {children} + {children} ); }; diff --git a/libs/components/editor-core/src/ref-page/index.ts b/libs/components/editor-core/src/ref-page/index.ts deleted file mode 100644 index 4b06310a5c..0000000000 --- a/libs/components/editor-core/src/ref-page/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { useRefPage, RefPageProvider } from './ModalPage'; From 4a99080860d15515345060483e59593b94632162 Mon Sep 17 00:00:00 2001 From: lawvs <18554747+lawvs@users.noreply.github.com> Date: Fri, 12 Aug 2022 13:52:54 +0800 Subject: [PATCH 14/24] chore: set kanban RefPageProvider --- .../blocks/group/scene-kanban/SceneKanban.tsx | 39 ++++++++++--------- 1 file changed, 21 insertions(+), 18 deletions(-) diff --git a/libs/components/editor-blocks/src/blocks/group/scene-kanban/SceneKanban.tsx b/libs/components/editor-blocks/src/blocks/group/scene-kanban/SceneKanban.tsx index 27d5543177..73424708a7 100644 --- a/libs/components/editor-blocks/src/blocks/group/scene-kanban/SceneKanban.tsx +++ b/libs/components/editor-blocks/src/blocks/group/scene-kanban/SceneKanban.tsx @@ -4,30 +4,33 @@ import { SceneKanbanContext } from './context'; import { CardContainerWrapper } from './dndable/wrapper/CardContainerWrapper'; import type { ComponentType } from 'react'; import type { CreateView } from '@toeverything/framework/virgo'; +import { RefPageProvider } from './RefPage'; export const SceneKanban: ComponentType = withKanban( ({ editor, block }) => { const { kanban } = useKanban(); return ( - - ( - - )} - /> - + + + ( + + )} + /> + + ); } ); From d300b039ad0f3e1fc7c5c227e44fb73ff4d88949 Mon Sep 17 00:00:00 2001 From: lawvs <18554747+lawvs@users.noreply.github.com> Date: Wed, 17 Aug 2022 19:07:55 +0800 Subject: [PATCH 15/24] fix: renaming to edgeless --- .../editor-blocks/src/blocks/group/scene-kanban/RefPage.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libs/components/editor-blocks/src/blocks/group/scene-kanban/RefPage.tsx b/libs/components/editor-blocks/src/blocks/group/scene-kanban/RefPage.tsx index cd3588162e..b3353001b0 100644 --- a/libs/components/editor-blocks/src/blocks/group/scene-kanban/RefPage.tsx +++ b/libs/components/editor-blocks/src/blocks/group/scene-kanban/RefPage.tsx @@ -53,7 +53,7 @@ const ModalPage = ({ blockId }: { blockId: string | null }) => { workspace={editor.workspace} rootBlockId={blockId} scrollBlank={false} - isWhiteboard + isEdgeless /> )} From 05361e75a405745e4a3f5c5328e9917ee47c6345 Mon Sep 17 00:00:00 2001 From: lawvs <18554747+lawvs@users.noreply.github.com> Date: Wed, 17 Aug 2022 19:08:16 +0800 Subject: [PATCH 16/24] fix: add pen background --- .../src/blocks/group/scene-kanban/CardItem.tsx | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/libs/components/editor-blocks/src/blocks/group/scene-kanban/CardItem.tsx b/libs/components/editor-blocks/src/blocks/group/scene-kanban/CardItem.tsx index c1cc9f9a1c..c98efdf0f5 100644 --- a/libs/components/editor-blocks/src/blocks/group/scene-kanban/CardItem.tsx +++ b/libs/components/editor-blocks/src/blocks/group/scene-kanban/CardItem.tsx @@ -1,5 +1,9 @@ -import type { KanbanCard } from '@toeverything/components/editor-core'; -import { RenderBlock, useKanban } from '@toeverything/components/editor-core'; +import { + KanbanCard, + RenderBlock, + useEditor, + useKanban, +} from '@toeverything/components/editor-core'; import { PenIcon } from '@toeverything/components/icons'; import { IconButton, @@ -89,6 +93,7 @@ export const CardItem = ({ const { openSubPage } = useRefPage(); const [editable, setEditable] = useState(false); const showKanbanRefPageFlag = useFlag('ShowKanbanRefPage', false); + const { editor } = useEditor(); const onAddItem = async () => { setEditable(true); @@ -108,9 +113,13 @@ export const CardItem = ({ {!editable && ( { e.stopPropagation(); setEditable(true); + editor.selectionManager.activeNodeByNodeId( + block.id + ); }} > From 06d442a8c18a773920ef10f4dfb8aa487d45f2a0 Mon Sep 17 00:00:00 2001 From: lawvs <18554747+lawvs@users.noreply.github.com> Date: Thu, 18 Aug 2022 17:09:28 +0800 Subject: [PATCH 17/24] refactor: clean anti pattern editor element --- .../src/blocks/embed-link/EmbedLinkView.tsx | 7 +++---- .../src/components/source-view/BlockView.tsx | 8 ++++---- .../src/components/source-view/SourceView.tsx | 17 ++++------------- .../editor-core/src/editor/views/base-view.ts | 1 - .../src/render-block/RenderBlock.tsx | 3 +-- 5 files changed, 12 insertions(+), 24 deletions(-) diff --git a/libs/components/editor-blocks/src/blocks/embed-link/EmbedLinkView.tsx b/libs/components/editor-blocks/src/blocks/embed-link/EmbedLinkView.tsx index 86881478ff..86b43908d1 100644 --- a/libs/components/editor-blocks/src/blocks/embed-link/EmbedLinkView.tsx +++ b/libs/components/editor-blocks/src/blocks/embed-link/EmbedLinkView.tsx @@ -1,12 +1,12 @@ -import { useState } from 'react'; -import { CreateView } from '@toeverything/framework/virgo'; import { BlockPendantProvider, useOnSelect, } from '@toeverything/components/editor-core'; -import { Upload } from '../../components/upload/upload'; +import { CreateView } from '@toeverything/framework/virgo'; +import { useState } from 'react'; import { SourceView } from '../../components/source-view'; import { LinkContainer } from '../../components/style-container'; +import { Upload } from '../../components/upload/upload'; const MESSAGES = { ADD_EMBED_LINK: 'Add embed link', @@ -38,7 +38,6 @@ export const EmbedLinkView = (props: EmbedLinkView) => { {embedLinkUrl ? ( { type BlockPreviewProps = { block: AsyncBlock; blockId: string; - editorElement?: () => JSX.Element; }; const InternalBlockPreview = (props: BlockPreviewProps) => { const container = useRef(); const [preview, setPreview] = useState(true); const title = useBlockTitle(props.block, props.blockId); + const { editorElement } = useEditor(); - const AffineEditor = props.editorElement as any; + const AffineEditor = editorElement as any; useEffect(() => { if (container?.current) { diff --git a/libs/components/editor-blocks/src/components/source-view/SourceView.tsx b/libs/components/editor-blocks/src/components/source-view/SourceView.tsx index b03189d7d4..27d19433bf 100644 --- a/libs/components/editor-blocks/src/components/source-view/SourceView.tsx +++ b/libs/components/editor-blocks/src/components/source-view/SourceView.tsx @@ -1,17 +1,15 @@ import { AsyncBlock, useCurrentView, - useLazyIframe, } from '@toeverything/components/editor-core'; import { styled } from '@toeverything/components/ui'; -import { ReactElement, ReactNode, useEffect, useRef, useState } from 'react'; +import { ReactNode, useEffect, useRef, useState } from 'react'; import { SCENE_CONFIG } from '../../blocks/group/config'; import { BlockPreview } from './BlockView'; import { formatUrl } from './format-url'; export interface Props { block: AsyncBlock; - editorElement?: () => JSX.Element; viewType?: string; link: string; // onResizeEnd: (data: any) => void; @@ -150,7 +148,7 @@ const LoadingContiner = () => { }; export const SourceView = (props: Props) => { - const { link, isSelected, block, editorElement } = props; + const { link, isSelected, block } = props; const src = formatUrl(link); // let iframeShow = useLazyIframe(src, 3000, iframeContainer); const [currentView] = useCurrentView(); @@ -161,10 +159,7 @@ export const SourceView = (props: Props) => { - + ); @@ -175,11 +170,7 @@ export const SourceView = (props: Props) => { style={{ padding: '0' }} scene={type} > - + ); } diff --git a/libs/components/editor-core/src/editor/views/base-view.ts b/libs/components/editor-core/src/editor/views/base-view.ts index b69cdd848a..eb5c293243 100644 --- a/libs/components/editor-core/src/editor/views/base-view.ts +++ b/libs/components/editor-core/src/editor/views/base-view.ts @@ -18,7 +18,6 @@ import { SelectBlock } from '../selection'; export interface CreateView { block: AsyncBlock; editor: Editor; - editorElement: () => JSX.Element; /** * @deprecated Use recast table instead */ diff --git a/libs/components/editor-core/src/render-block/RenderBlock.tsx b/libs/components/editor-core/src/render-block/RenderBlock.tsx index 0627aba95b..0a1531a709 100644 --- a/libs/components/editor-core/src/render-block/RenderBlock.tsx +++ b/libs/components/editor-core/src/render-block/RenderBlock.tsx @@ -13,7 +13,7 @@ export function RenderBlock({ blockId, hasContainer = true, }: RenderBlockProps) { - const { editor, editorElement } = useEditor(); + const { editor } = useEditor(); const { block } = useBlock(blockId); const setRef = useCallback( @@ -50,7 +50,6 @@ export function RenderBlock({ block={block} columns={columns.columns} columnsFromId={columns.fromId} - editorElement={editorElement} /> ); From 0d89fa10261aaeb55160f320e71b7a8821c8d95b Mon Sep 17 00:00:00 2001 From: lawvs <18554747+lawvs@users.noreply.github.com> Date: Thu, 18 Aug 2022 17:11:39 +0800 Subject: [PATCH 18/24] fix: workaround circular dependency editor element --- .../editor-blocks/src/blocks/group/scene-kanban/RefPage.tsx | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/libs/components/editor-blocks/src/blocks/group/scene-kanban/RefPage.tsx b/libs/components/editor-blocks/src/blocks/group/scene-kanban/RefPage.tsx index b3353001b0..3d223d121d 100644 --- a/libs/components/editor-blocks/src/blocks/group/scene-kanban/RefPage.tsx +++ b/libs/components/editor-blocks/src/blocks/group/scene-kanban/RefPage.tsx @@ -1,4 +1,3 @@ -import { AffineEditor } from '@toeverything/components/affine-editor'; import { useEditor } from '@toeverything/components/editor-core'; import { MuiBackdrop, styled, useTheme } from '@toeverything/components/ui'; import { createContext, ReactNode, useContext, useState } from 'react'; @@ -44,7 +43,9 @@ const Modal = ({ open, children }: { open: boolean; children?: ReactNode }) => { }; const ModalPage = ({ blockId }: { blockId: string | null }) => { - const { editor } = useEditor(); + const { editor, editorElement } = useEditor(); + + const AffineEditor = editorElement as any; return ( @@ -53,6 +54,7 @@ const ModalPage = ({ blockId }: { blockId: string | null }) => { workspace={editor.workspace} rootBlockId={blockId} scrollBlank={false} + // use edgeless mode prevent padding and blank bottom isEdgeless /> )} From 42cc6e90420f89d5f2868accc539b0150b63eb75 Mon Sep 17 00:00:00 2001 From: lawvs <18554747+lawvs@users.noreply.github.com> Date: Thu, 18 Aug 2022 17:59:21 +0800 Subject: [PATCH 19/24] chore: clean card item --- .../blocks/group/scene-kanban/CardItem.tsx | 23 ++++++++----------- 1 file changed, 10 insertions(+), 13 deletions(-) diff --git a/libs/components/editor-blocks/src/blocks/group/scene-kanban/CardItem.tsx b/libs/components/editor-blocks/src/blocks/group/scene-kanban/CardItem.tsx index c98efdf0f5..837772bed2 100644 --- a/libs/components/editor-blocks/src/blocks/group/scene-kanban/CardItem.tsx +++ b/libs/components/editor-blocks/src/blocks/group/scene-kanban/CardItem.tsx @@ -11,7 +11,7 @@ import { styled, } from '@toeverything/components/ui'; import { useFlag } from '@toeverything/datasource/feature-flags'; -import { useState } from 'react'; +import { useState, type MouseEvent } from 'react'; import { useRefPage } from './RefPage'; const CardContent = styled('div')({ @@ -101,7 +101,13 @@ export const CardItem = ({ }; const onClickCard = async () => { - showKanbanRefPageFlag && openSubPage(id); + openSubPage(id); + }; + + const onClickPen = (e: MouseEvent) => { + e.stopPropagation(); + setEditable(true); + editor.selectionManager.activeNodeByNodeId(block.id); }; return ( @@ -110,18 +116,9 @@ export const CardItem = ({ - {!editable && ( + {showKanbanRefPageFlag && !editable && ( - { - e.stopPropagation(); - setEditable(true); - editor.selectionManager.activeNodeByNodeId( - block.id - ); - }} - > + From d1835e8cad65d56672d0fff44c1ded90e91a16f5 Mon Sep 17 00:00:00 2001 From: QiShaoXuan Date: Thu, 18 Aug 2022 18:20:07 +0800 Subject: [PATCH 20/24] 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 6915c9c2a5084ff9612dc5b6a51c9105d91c6bdb Mon Sep 17 00:00:00 2001 From: austaras Date: Thu, 18 Aug 2022 17:26:11 +0800 Subject: [PATCH 21/24] fix(whiteboard): only allow pan when middle button is pressed --- libs/components/board-state/src/tldraw-app.ts | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/libs/components/board-state/src/tldraw-app.ts b/libs/components/board-state/src/tldraw-app.ts index e4b7a8a34f..5dd9ad09b2 100644 --- a/libs/components/board-state/src/tldraw-app.ts +++ b/libs/components/board-state/src/tldraw-app.ts @@ -3841,7 +3841,7 @@ export class TldrawApp extends StateManager { private get_viewbox_from_svg = (svgStr: string | ArrayBuffer | null) => { if (typeof svgStr === 'string') { - let viewBox = new DOMParser().parseFromString(svgStr, 'text/xml'); + const viewBox = new DOMParser().parseFromString(svgStr, 'text/xml'); return viewBox.children[0].getAttribute('viewBox'); } @@ -4125,7 +4125,7 @@ export class TldrawApp extends StateManager { }; onPointerDown: TLPointerEventHandler = (info, e) => { - if (e.buttons === 4) { + if (e.button === 1) { this.patchState({ settings: { forcePanning: true, @@ -4142,6 +4142,13 @@ export class TldrawApp extends StateManager { }; onPointerUp: TLPointerEventHandler = (info, e) => { + if (e.button === 1) { + this.patchState({ + settings: { + forcePanning: false, + }, + }); + } this.isPointing = false; this.updateInputs(info, e); this.currentTool.onPointerUp?.(info, e); 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 22/24] 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 23/24] 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); From 60059ea00a1d6f6323d7514da4fab72d07962dc0 Mon Sep 17 00:00:00 2001 From: Wang Yu Date: Fri, 19 Aug 2022 14:17:46 +0800 Subject: [PATCH 24/24] fix: remove unused executor svgOptimize (#296) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: 王昱 --- tools/executors/svgOptimize/executor.json | 9 -- tools/executors/svgOptimize/package.json | 3 - tools/executors/svgOptimize/schema.json | 11 -- tools/executors/svgOptimize/svgo.js | 133 ---------------------- 4 files changed, 156 deletions(-) delete mode 100644 tools/executors/svgOptimize/executor.json delete mode 100644 tools/executors/svgOptimize/package.json delete mode 100644 tools/executors/svgOptimize/schema.json delete mode 100644 tools/executors/svgOptimize/svgo.js diff --git a/tools/executors/svgOptimize/executor.json b/tools/executors/svgOptimize/executor.json deleted file mode 100644 index 1c19a2f9c5..0000000000 --- a/tools/executors/svgOptimize/executor.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "executors": { - "svgOptimize": { - "implementation": "./svgo.js", - "schema": "./schema.json", - "description": "Run `svgo` (to optimize svg)." - } - } -} diff --git a/tools/executors/svgOptimize/package.json b/tools/executors/svgOptimize/package.json deleted file mode 100644 index 721ff1df5d..0000000000 --- a/tools/executors/svgOptimize/package.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "executors": "./executor.json" -} diff --git a/tools/executors/svgOptimize/schema.json b/tools/executors/svgOptimize/schema.json deleted file mode 100644 index dc2fdc029b..0000000000 --- a/tools/executors/svgOptimize/schema.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "$schema": "http://json-schema.org/schema", - "type": "object", - "cli": "nx", - "properties": { - "svgOptimize": { - "type": "string", - "description": "optimize svg" - } - } -} diff --git a/tools/executors/svgOptimize/svgo.js b/tools/executors/svgOptimize/svgo.js deleted file mode 100644 index a0f6c21539..0000000000 --- a/tools/executors/svgOptimize/svgo.js +++ /dev/null @@ -1,133 +0,0 @@ -const path = require('path'); -const svgo = require('svgo'); -const { readdir, readFile, writeFile, exists } = require('fs/promises'); -const { pascalCase, paramCase } = require('change-case'); -const svgr = require('@svgr/core'); - -async function optimizeSvg(folder) { - try { - const icons = await readdir(folder); - const generateIcons = icons - .filter(n => n.endsWith('.svg')) - .map(async icon => { - let originSvg; - try { - originSvg = await readFile(path.resolve(folder, icon)); - } catch (err) { - console.error(err); - } - let optimizedSvg; - try { - const data = optimize(originSvg); - optimizedSvg = data.data; - } catch (err) { - console.error(err); - } - - const JSXContent = await getJSXContent( - pascalCase(icon), - optimizedSvg - ); - - const iconName = path.basename(icon, '.svg'); - await writeFile( - path.resolve(folder, `${iconName}.tsx`), - JSXContent, - { encoding: 'utf8', flag: '' } - ); - - console.log('Generated:', iconName); - }); - - await Promise.allSettled([ - ...generateIcons, - generateImportEntry(icons, folder), - ]); - } catch (err) { - console.error(err); - } -} - -function optimize(input) { - return svgo.optimize(input, { - plugins: [ - 'preset-default', - 'prefixIds', - { - name: 'sortAttrs', - params: { - xmlnsOrder: 'alphabetical', - }, - }, - ], - }); -} - -/** - * get icon component template - * - * @param {string} name - */ -async function getJSXContent(name, svgCode) { - let svgrContent = ''; - try { - svgrContent = await svgr.transform( - svgCode, - { - icon: true, - typescript: true, - }, - { componentName: `${name}Icon1` } - ); - } catch (err) { - console.error(err); - } - let matcher = svgrContent.match(/]+)>([\s\S]*?)<\/svg>/); - return ` -import { SvgIcon, SvgIconProps } from '@mui/material'; -export const ${name}Icon = (props: SvgIconProps) => ( - - ${matcher[2]} - -); -`; -} - -async function generateImportEntry(iconNodes, folder) { - const fileWithImportsPath = path.resolve(folder, 'index.ts'); - - const importsContent = iconNodes - .map(iconNode => { - const iconName = paramCase(iconNode.name); - if (!iconName) { - return `// Error: ${iconNode.name}`; - } - - return `export * from './${iconName}/${iconName}';`; - }) - .join('\n'); - - await fs.writeFile( - fileWithImportsPath, - `export const timestamp = ${Date.now()};\n${importsContent}`, - { encoding: 'utf8' } - ); -} - -/** - * @param {*} options - * @param {array} options.assets - * @param {string} options.assets.folder - * @param {*} context - * @returns - */ -exports['default'] = async function svgo(options, context) { - const libRoot = context.workspace.projects[context.projectName].root; - await Promise.allSettled( - (options.assets || []).map(async (asset, index) => { - await optimizeSvg(path.resolve(libRoot, asset.folder)); - }) - ); - - return { success: true }; -};