From ebdba2e2deffcdf583034fa875edb5efe53c723d Mon Sep 17 00:00:00 2001 From: Zhilin Liu Date: Mon, 15 Jan 2024 18:21:15 +0800 Subject: [PATCH] feat: clipboard (#64) --- src/BlockHub/Block/Block.ts | 2 + src/RichText/RichText.ts | 11 +++++ src/RichText/handler/EventHandler.ts | 1 - src/RichText/handler/utils/handleInput.ts | 22 ++++++++++ .../ToolBar/components/Home/Clipboard.vue | 43 ++++++++++++++++--- 5 files changed, 73 insertions(+), 6 deletions(-) diff --git a/src/BlockHub/Block/Block.ts b/src/BlockHub/Block/Block.ts index 743f958..8211d88 100644 --- a/src/BlockHub/Block/Block.ts +++ b/src/BlockHub/Block/Block.ts @@ -98,6 +98,8 @@ export class Block { isFocus: () => false, getCommonAttributes: () => ({}), format: (name: AttributeName, value: AttributeValue) => {}, + delete: () => {}, + insert: (text: string) => {}, } } } diff --git a/src/RichText/RichText.ts b/src/RichText/RichText.ts index bcd2313..66ed3c3 100644 --- a/src/RichText/RichText.ts +++ b/src/RichText/RichText.ts @@ -3,11 +3,14 @@ import { Selection, SelectionHandler } from './handler/SelectionHandler' import { EventHandler } from './handler/EventHandler' import { EventManager } from '@Kernel/EventManager' import { richTextObserver } from '@Kernel/index' +import { handleInput } from './handler/utils/handleInput' export interface RichTextController { isFocus(): boolean getCommonAttributes(): Attributes format(name: AttributeName, value: AttributeValue): void + delete(): void + insert(text: string): void } export class RichText { @@ -72,6 +75,14 @@ export class RichText { } this.events.formatChange.emit({ index, length }) }, + delete: () => { + // for clipboard cut + handleInput('deleteContentBackward', this, { text: '', attributes: {} }) + }, + insert: (text: string) => { + // for clipboard paste + handleInput('insertText', this, { text: text, attributes: {} }) + }, } } } diff --git a/src/RichText/handler/EventHandler.ts b/src/RichText/handler/EventHandler.ts index 94bf7cf..c8f0e28 100644 --- a/src/RichText/handler/EventHandler.ts +++ b/src/RichText/handler/EventHandler.ts @@ -1,6 +1,5 @@ import { type RichText } from '@RichText/RichText' import { handleInput } from './utils/handleInput' -import { sleep } from '@Utils/sleep' export class EventHandler { richText: RichText diff --git a/src/RichText/handler/utils/handleInput.ts b/src/RichText/handler/utils/handleInput.ts index 45bbbc9..cf09602 100644 --- a/src/RichText/handler/utils/handleInput.ts +++ b/src/RichText/handler/utils/handleInput.ts @@ -28,6 +28,14 @@ function handleInsertParagraph(richText: RichText, atom: TextAtom) { }) } +async function handleInsertFromPaste(richText: RichText) { + const text = await navigator.clipboard.readText() + handleInsertText(richText, { + text: text, + attributes: {}, + }) +} + function handleDeleteContentBackward(richText: RichText, atom: TextAtom) { const { index, length } = richText.getSelection() if (length === 0) { @@ -45,11 +53,25 @@ function handleDeleteContentBackward(richText: RichText, atom: TextAtom) { } } +function handleDeleteByCut(richText: RichText, atom: TextAtom) { + const { index, length } = richText.getSelection() + if (length === 0) { + return + } + richText.textStore.delete(index, length) + richText.setSelectionByInput({ + index: index, + length: 0, + }) +} + export function handleInput(inputType: string, richText: RichText, atom: TextAtom) { const handlers: { [key: string]: (richText: RichText, atom: TextAtom) => void } = { insertText: handleInsertText, insertParagraph: handleInsertParagraph, + insertFromPaste: handleInsertFromPaste, deleteContentBackward: handleDeleteContentBackward, + deleteByCut: handleDeleteByCut, } handlers[inputType](richText, atom) } diff --git a/src/UserInterface/ToolBar/components/Home/Clipboard.vue b/src/UserInterface/ToolBar/components/Home/Clipboard.vue index 7360ae1..5c5f265 100644 --- a/src/UserInterface/ToolBar/components/Home/Clipboard.vue +++ b/src/UserInterface/ToolBar/components/Home/Clipboard.vue @@ -2,12 +2,45 @@ import { Notepad, CuttingOne, CopyOne, Format } from '@icon-park/vue-next' import ButtonGroup from '../ButtonGroup.vue' import ToolButton from '../ToolButton.vue' +import { richTextObserver, selectionManager } from '@Kernel/index' +import { ref, shallowRef, computed } from 'vue' + +const selectedText = ref('') +richTextObserver.on(() => { + selectedText.value = window.getSelection()?.toString() as string +}) + +const copy = async () => { + const type = 'text/plain' + const blob = new Blob([selectedText.value], { type }) + const data = [new ClipboardItem({ [type]: blob })] + await navigator.clipboard.write(data) +} + +const cut = () => { + copy() + const currentBlock = selectionManager.selectedBlocks[0] + currentBlock.getController().delete() +} + +const selectedBlocks = shallowRef(selectionManager.selectedBlocks) +selectionManager.events.update.on(() => { + selectedBlocks.value = selectionManager.selectedBlocks +}) +const canPaste = computed(() => { + return selectedBlocks.value.length === 1 && selectedBlocks.value[0].type === 'TextBox' +}) +const paste = async () => { + const text = await navigator.clipboard.readText() + const currentBlock = selectionManager.selectedBlocks[0] + currentBlock.getController().insert(text) +}