-
Notifications
You must be signed in to change notification settings - Fork 4
feat(core): implement Clipboard Plugin #129
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 11 commits
9dd6f8e
b53c482
0be2ca4
10c1c22
ad5b441
e1359c8
500178d
bc3657c
00d10ac
d426ae4
31a5758
9f44277
f63d6dd
55b62c8
2681e5b
2b288dc
f2a6067
264f721
f4601a6
ec4aca5
c144d28
b7b617f
40a9b2b
9de3ad6
158abf6
c163812
2cf7dfe
a4365c3
4dff6fa
9d0f896
18963e9
c322b46
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,76 @@ | ||
| import type { CopyUIEvent, EditorAPI, EditorjsPlugin, EditorjsPluginParams } from '@editorjs/sdk'; | ||
| import { CopyUIEventName } from '@editorjs/sdk'; | ||
| import { PluginType } from '@editorjs/sdk'; | ||
|
|
||
| /** | ||
| * @todo update doc | ||
| */ | ||
| export class ClipboardPlugin implements EditorjsPlugin { | ||
| public static readonly type = PluginType.Plugin; | ||
|
|
||
| readonly #api: EditorAPI; | ||
|
|
||
| /** | ||
| * @param params @todo update doc | ||
| */ | ||
| constructor(params: EditorjsPluginParams) { | ||
| const { api, eventBus } = params; | ||
|
|
||
| this.#api = api; | ||
|
|
||
| eventBus.addEventListener(`ui:${CopyUIEventName}`, (e: CopyUIEvent) => { | ||
| const { nativeEvent } = e.detail; | ||
|
|
||
| const selectedBlocks = this.#api.selection.selectedBlocks; | ||
|
|
||
| /** | ||
| * Don't override native event if there are no blocks selected | ||
| */ | ||
| if (selectedBlocks === null || selectedBlocks.length === 0) { | ||
| return; | ||
| } | ||
|
|
||
| const currentDOMSelection = window.getSelection(); | ||
|
|
||
| if (!currentDOMSelection) { | ||
| return; | ||
| } | ||
|
|
||
| const selectionAsPlainText = currentDOMSelection.toString(); | ||
| const selectionAsHTML = this.#parseDOMSelectionToHTML(currentDOMSelection); | ||
|
|
||
| nativeEvent.preventDefault(); | ||
|
|
||
| nativeEvent.clipboardData?.setData('text/plain', selectionAsPlainText); | ||
| nativeEvent.clipboardData?.setData('text/html', selectionAsHTML); | ||
| nativeEvent.clipboardData?.setData('application/x-editor-js', JSON.stringify(selectedBlocks)); | ||
|
ilyamore88 marked this conversation as resolved.
Outdated
ilyamore88 marked this conversation as resolved.
Outdated
|
||
| }); | ||
| } | ||
|
|
||
| /** | ||
| * @todo update doc | ||
| */ | ||
| public destroy(): void { | ||
| // do nothing | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Could unsubscribe from the copy event
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Resolved |
||
| } | ||
|
|
||
| /** | ||
| * | ||
| * @param selection | ||
| */ | ||
| #parseDOMSelectionToHTML(selection: Selection): string { | ||
| if (selection.rangeCount === 0) { | ||
| return ''; | ||
| } | ||
|
|
||
| const container = document.createElement('div'); | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Template might be better instead of div
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Resolved |
||
|
|
||
| for (let i = 0; i < selection.rangeCount; i++) { | ||
| const range = selection.getRangeAt(i); | ||
|
|
||
| container.appendChild(range.cloneContents()); | ||
| } | ||
|
|
||
| return container.innerHTML; | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,4 +1,4 @@ | ||
| import type { Caret, CaretManagerEvents } from '@editorjs/model'; | ||
| import type { Caret, CaretManagerEvents, BlockNodeSerialized } from '@editorjs/model'; | ||
|
|
||
| /** | ||
| * Selection API interface | ||
|
|
@@ -31,4 +31,9 @@ export interface SelectionAPI { | |
| * @param userId - user id. If not provided, returns for current user | ||
| */ | ||
| getCaret(userId?: string | number): Caret | undefined; | ||
|
|
||
| /** | ||
| * | ||
|
ilyamore88 marked this conversation as resolved.
Outdated
|
||
| */ | ||
| get selectedBlocks(): BlockNodeSerialized[] | null; | ||
|
ilyamore88 marked this conversation as resolved.
Outdated
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe [] instead of null
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Resolved |
||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,29 @@ | ||
| import { UIEventBase } from './UIEventBase.js'; | ||
|
|
||
| /** | ||
| * Name of the copy UI event (dispatched as `ui:copy`) | ||
| */ | ||
| export const CopyUIEventName = 'copy'; | ||
|
|
||
| /** | ||
| * Payload @todo update doc | ||
| */ | ||
| export interface CopyUIEventPayload { | ||
| /** | ||
| * Native ClipboardEvent | ||
| * UI does not call .preventDefault() for this event | ||
| */ | ||
| nativeEvent: ClipboardEvent; | ||
| } | ||
|
|
||
| /** | ||
| * Delegated copy event from the editor @todo update doc | ||
| */ | ||
|
ilyamore88 marked this conversation as resolved.
|
||
| export class CopyUIEvent extends UIEventBase<CopyUIEventPayload> { | ||
|
ilyamore88 marked this conversation as resolved.
|
||
| /** | ||
| * @param payload - carries the original DOM `ClipboardEvent` as `nativeEvent` for providing rich clipboard data | ||
|
ilyamore88 marked this conversation as resolved.
Outdated
|
||
| */ | ||
| constructor(payload: CopyUIEventPayload) { | ||
| super(CopyUIEventName, payload); | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,3 +1,4 @@ | ||
| export * from './UIEventBase.js'; | ||
| export * from './BeforeInputUIEvent.js'; | ||
| export * from './KeydownUIEvent.js'; | ||
| export * from './CopyUIEvent.js'; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,6 +1,6 @@ | ||
| import type { | ||
| BlockAddedCoreEvent, | ||
| BlockRemovedCoreEvent, EditorAPI, | ||
| BlockRemovedCoreEvent, CopyUIEvent, EditorAPI, | ||
| EditorjsPlugin, | ||
| EditorjsPluginParams | ||
|
ilyamore88 marked this conversation as resolved.
Outdated
|
||
| } from '@editorjs/sdk'; | ||
|
|
@@ -9,7 +9,11 @@ import { | |
| UiComponentType, | ||
| BeforeInputUIEvent | ||
| } from '@editorjs/sdk'; | ||
| import type { EventBus } from '@editorjs/sdk'; | ||
| import type { EventBus, | ||
| BlockAddedCoreEvent, | ||
| BlockRemovedCoreEvent, CopyUIEventPayload, | ||
| EditorjsPlugin, | ||
| EditorjsPluginParams } from '@editorjs/sdk'; | ||
| import Style from './Blocks.module.pcss'; | ||
| import { isNativeInput, make } from '@editorjs/dom'; | ||
| import { BlocksHolderRenderedUIEvent, BlockSelectedUIEvent } from './events/index.js'; | ||
|
|
@@ -138,6 +142,14 @@ export class BlocksUI implements EditorjsPlugin { | |
| e.preventDefault(); | ||
| }); | ||
|
|
||
| blocksHolder.addEventListener('copy', (e) => { | ||
| const payload: CopyUIEventPayload = { | ||
| nativeEvent: e, | ||
| }; | ||
|
|
||
| this.#eventBus.dispatchEvent(new CopyUIEvent(payload)); | ||
| }); | ||
|
Comment on lines
+140
to
+146
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Tracked in #170 |
||
|
|
||
| return blocksHolder; | ||
| } | ||
|
|
||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.