ckeditor5-elements-clipboard 0.0.0-stage → 49.0.0-alpha.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +27 -2
- package/dist/augmentation.d.ts +14 -0
- package/dist/augmentation.d.ts.map +1 -0
- package/dist/augmentation.js +2 -0
- package/dist/augmentation.js.map +1 -0
- package/dist/elementClipboard.d.ts +18 -0
- package/dist/elementClipboard.d.ts.map +1 -0
- package/dist/elementClipboard.js +32 -0
- package/dist/elementClipboard.js.map +1 -0
- package/dist/elementCopyCommand.d.ts +30 -0
- package/dist/elementCopyCommand.d.ts.map +1 -0
- package/dist/elementCopyCommand.js +64 -0
- package/dist/elementCopyCommand.js.map +1 -0
- package/dist/elementPasteCommand.d.ts +38 -0
- package/dist/elementPasteCommand.d.ts.map +1 -0
- package/dist/elementPasteCommand.js +100 -0
- package/dist/elementPasteCommand.js.map +1 -0
- package/dist/icons.d.ts +24 -0
- package/dist/icons.d.ts.map +1 -0
- package/dist/icons.js +34 -0
- package/dist/icons.js.map +1 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +14 -0
- package/dist/index.js.map +1 -0
- package/dist/utils.d.ts +7 -0
- package/dist/utils.d.ts.map +1 -0
- package/dist/utils.js +8 -0
- package/dist/utils.js.map +1 -0
- package/package.json +45 -3
- package/src/augmentation.ts +15 -0
- package/src/elementClipboard.ts +35 -0
- package/src/elementCopyCommand.ts +75 -0
- package/src/elementPasteCommand.ts +129 -0
- package/src/icons.ts +43 -0
- package/src/index.ts +17 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Ivan Kildyushov
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,28 @@
|
|
|
1
|
-
#
|
|
1
|
+
# ckeditor5-elements-clipboard
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Clipboard commands for CKEditor 5 Elements: `ElementClipboard` registers `copyElement`, `cutElement`, `pasteElement`,
|
|
4
|
+
`pasteElementBefore` and `pasteElementAfter`, which act on a whole element, through the CKEditor clipboard pipeline.
|
|
5
|
+
|
|
6
|
+
## Installation
|
|
7
|
+
|
|
8
|
+
This package is part of `ckeditor5-elements`. Install the whole package to use it:
|
|
9
|
+
|
|
10
|
+
```sh
|
|
11
|
+
npm install ckeditor5 ckeditor5-elements
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
## Usage
|
|
15
|
+
|
|
16
|
+
```ts
|
|
17
|
+
import { ElementClipboard } from 'ckeditor5-elements';
|
|
18
|
+
|
|
19
|
+
// plugins: [..., ElementClipboard]
|
|
20
|
+
await editor.execute('copyElement', element);
|
|
21
|
+
await editor.execute('pasteElementAfter'); // The element at the selection.
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
See the [Clipboard guide](https://ivankild.github.io/ckeditor5-elements/guide/clipboard) of the documentation.
|
|
25
|
+
|
|
26
|
+
## License
|
|
27
|
+
|
|
28
|
+
MIT
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { ElementClipboard, ElementCopyCommand, ElementPasteCommand } from './index.js';
|
|
2
|
+
declare module 'ckeditor5' {
|
|
3
|
+
interface PluginsMap {
|
|
4
|
+
[ElementClipboard.pluginName]: ElementClipboard;
|
|
5
|
+
}
|
|
6
|
+
interface CommandsMap {
|
|
7
|
+
copyElement: ElementCopyCommand;
|
|
8
|
+
cutElement: ElementCopyCommand;
|
|
9
|
+
pasteElement: ElementPasteCommand;
|
|
10
|
+
pasteElementBefore: ElementPasteCommand;
|
|
11
|
+
pasteElementAfter: ElementPasteCommand;
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
//# sourceMappingURL=augmentation.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"augmentation.d.ts","sourceRoot":"","sources":["../src/augmentation.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AAE5F,OAAO,QAAQ,WAAW,CAAC;IAC1B,UAAU,UAAU;QACnB,CAAC,gBAAgB,CAAC,UAAU,CAAC,EAAE,gBAAgB,CAAC;KAChD;IAED,UAAU,WAAW;QACpB,WAAW,EAAE,kBAAkB,CAAC;QAChC,UAAU,EAAE,kBAAkB,CAAC;QAC/B,YAAY,EAAE,mBAAmB,CAAC;QAClC,kBAAkB,EAAE,mBAAmB,CAAC;QACxC,iBAAiB,EAAE,mBAAmB,CAAC;KACvC;CACD"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"augmentation.js","sourceRoot":"","sources":["../src/augmentation.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { ClipboardPipeline, Notification, Plugin } from 'ckeditor5';
|
|
2
|
+
/**
|
|
3
|
+
* Registers clipboard commands that act on an element:
|
|
4
|
+
* `copyElement`, `cutElement`, `pasteElement`, `pasteElementBefore` and `pasteElementAfter`.
|
|
5
|
+
*
|
|
6
|
+
* ```ts
|
|
7
|
+
* await editor.execute('copyElement', element);
|
|
8
|
+
* ```
|
|
9
|
+
*
|
|
10
|
+
* The commands have titles and icons, so they can be menu items (e.g. in the `elementContextMenu` config of
|
|
11
|
+
* `ElementContextMenu`) and buttons. Copying and pasting go through the clipboard pipeline, as Ctrl+C and Ctrl+V do.
|
|
12
|
+
*/
|
|
13
|
+
export default class ElementClipboard extends Plugin {
|
|
14
|
+
static get requires(): readonly [typeof ClipboardPipeline, typeof Notification];
|
|
15
|
+
static get pluginName(): 'ElementClipboard';
|
|
16
|
+
init(): void;
|
|
17
|
+
}
|
|
18
|
+
//# sourceMappingURL=elementClipboard.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"elementClipboard.d.ts","sourceRoot":"","sources":["../src/elementClipboard.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,YAAY,EAAE,MAAM,EAAE,MAAM,WAAW,CAAC;AAIpE;;;;;;;;;;GAUG;AACH,MAAM,CAAC,OAAO,OAAO,gBAAiB,SAAQ,MAAM;IACnD,WAAkB,QAAQ,IAAI,SAAS,CAAC,OAAO,iBAAiB,EAAE,OAAO,YAAY,CAAC,CAGrF;IAED,WAAkB,UAAU,IAAI,kBAAkB,CAEjD;IAEM,IAAI,IAAI,IAAI;CASnB"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { ClipboardPipeline, Notification, Plugin } from 'ckeditor5';
|
|
2
|
+
import ElementCopyCommand from './elementCopyCommand.js';
|
|
3
|
+
import ElementPasteCommand from './elementPasteCommand.js';
|
|
4
|
+
/**
|
|
5
|
+
* Registers clipboard commands that act on an element:
|
|
6
|
+
* `copyElement`, `cutElement`, `pasteElement`, `pasteElementBefore` and `pasteElementAfter`.
|
|
7
|
+
*
|
|
8
|
+
* ```ts
|
|
9
|
+
* await editor.execute('copyElement', element);
|
|
10
|
+
* ```
|
|
11
|
+
*
|
|
12
|
+
* The commands have titles and icons, so they can be menu items (e.g. in the `elementContextMenu` config of
|
|
13
|
+
* `ElementContextMenu`) and buttons. Copying and pasting go through the clipboard pipeline, as Ctrl+C and Ctrl+V do.
|
|
14
|
+
*/
|
|
15
|
+
export default class ElementClipboard extends Plugin {
|
|
16
|
+
static get requires() {
|
|
17
|
+
// The buttons of the commands report failures (e.g. no clipboard permission) with notifications.
|
|
18
|
+
return [ClipboardPipeline, Notification];
|
|
19
|
+
}
|
|
20
|
+
static get pluginName() {
|
|
21
|
+
return 'ElementClipboard';
|
|
22
|
+
}
|
|
23
|
+
init() {
|
|
24
|
+
const editor = this.editor;
|
|
25
|
+
editor.commands.add('copyElement', new ElementCopyCommand(editor));
|
|
26
|
+
editor.commands.add('cutElement', new ElementCopyCommand(editor, { cut: true }));
|
|
27
|
+
editor.commands.add('pasteElement', new ElementPasteCommand(editor, 'replace'));
|
|
28
|
+
editor.commands.add('pasteElementBefore', new ElementPasteCommand(editor, 'before'));
|
|
29
|
+
editor.commands.add('pasteElementAfter', new ElementPasteCommand(editor, 'after'));
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
//# sourceMappingURL=elementClipboard.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"elementClipboard.js","sourceRoot":"","sources":["../src/elementClipboard.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,YAAY,EAAE,MAAM,EAAE,MAAM,WAAW,CAAC;AACpE,OAAO,kBAAkB,MAAM,yBAAyB,CAAC;AACzD,OAAO,mBAAmB,MAAM,0BAA0B,CAAC;AAE3D;;;;;;;;;;GAUG;AACH,MAAM,CAAC,OAAO,OAAO,gBAAiB,SAAQ,MAAM;IAC5C,MAAM,KAAK,QAAQ;QACzB,iGAAiG;QACjG,OAAO,CAAC,iBAAiB,EAAE,YAAY,CAAU,CAAC;IACnD,CAAC;IAEM,MAAM,KAAK,UAAU;QAC3B,OAAO,kBAA2B,CAAC;IACpC,CAAC;IAEM,IAAI;QACV,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC;QAE3B,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC,aAAa,EAAE,IAAI,kBAAkB,CAAC,MAAM,CAAC,CAAC,CAAC;QACnE,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC,YAAY,EAAE,IAAI,kBAAkB,CAAC,MAAM,EAAE,EAAE,GAAG,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;QACjF,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC,cAAc,EAAE,IAAI,mBAAmB,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC,CAAC;QAChF,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC,oBAAoB,EAAE,IAAI,mBAAmB,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC,CAAC;QACrF,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC,mBAAmB,EAAE,IAAI,mBAAmB,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;IACpF,CAAC;CACD"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { type Editor } from 'ckeditor5';
|
|
2
|
+
import { ElementOrPositionCommand, type ElementOrPosition } from 'ckeditor5-elements-core';
|
|
3
|
+
/**
|
|
4
|
+
* Copies an element to the system clipboard, as HTML and plain text. With `cut: true`, it also removes the element.
|
|
5
|
+
*
|
|
6
|
+
* The element is the given one, the parent of the given position, or the selected element or the parent of the
|
|
7
|
+
* selection (see {@link ElementOrPositionCommand}). Parts of a structure, such as table cells, rows and captions, cannot
|
|
8
|
+
* be copied on their own, so the command is disabled for them.
|
|
9
|
+
*
|
|
10
|
+
* The content goes through the clipboard pipeline, as with Ctrl+C: other plugins can change it (they listen to the
|
|
11
|
+
* `outputTransformation` event of `ClipboardPipeline`, and to `clipboardOutput` of the editing view document).
|
|
12
|
+
*
|
|
13
|
+
* Requires a secure context (HTTPS or `localhost`), as it uses the asynchronous Clipboard API.
|
|
14
|
+
*/
|
|
15
|
+
export default class ElementCopyCommand extends ElementOrPositionCommand {
|
|
16
|
+
/**
|
|
17
|
+
* Whether the element is removed after copying.
|
|
18
|
+
*/
|
|
19
|
+
readonly cut: boolean;
|
|
20
|
+
constructor(editor: Editor, { cut }?: {
|
|
21
|
+
cut?: boolean;
|
|
22
|
+
});
|
|
23
|
+
protected _isEnabledFor(elementOrPosition?: ElementOrPosition): boolean;
|
|
24
|
+
/**
|
|
25
|
+
* Copies the element (and removes it, with `cut: true`). Resolves when the clipboard has been written; rejects when
|
|
26
|
+
* the browser does not allow it.
|
|
27
|
+
*/
|
|
28
|
+
execute(elementOrPosition?: ElementOrPosition): Promise<void>;
|
|
29
|
+
}
|
|
30
|
+
//# sourceMappingURL=elementCopyCommand.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"elementCopyCommand.d.ts","sourceRoot":"","sources":["../src/elementCopyCommand.ts"],"names":[],"mappings":"AAAA,OAAO,EAAgF,KAAK,MAAM,EAAE,MAAM,WAAW,CAAC;AACtH,OAAO,EAAE,wBAAwB,EAA8B,KAAK,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AAGvH;;;;;;;;;;;GAWG;AACH,MAAM,CAAC,OAAO,OAAO,kBAAmB,SAAQ,wBAAwB;IACvE;;OAEG;IACH,SAAgB,GAAG,EAAE,OAAO,CAAC;gBAEjB,MAAM,EAAE,MAAM,EAAE,EAAE,GAAW,EAAE,GAAE;QAAE,GAAG,CAAC,EAAE,OAAO,CAAA;KAAO;cAWhD,aAAa,CAAC,iBAAiB,CAAC,EAAE,iBAAiB,GAAG,OAAO;IAMhF;;;OAGG;IACmB,OAAO,CAAC,iBAAiB,CAAC,EAAE,iBAAiB,GAAG,OAAO,CAAC,IAAI,CAAC;CA+BnF"}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { ClipboardPipeline, ViewDataTransfer } from 'ckeditor5';
|
|
2
|
+
import { ElementOrPositionCommand, isInDocument, isStandalone } from 'ckeditor5-elements-core';
|
|
3
|
+
import { copyIcon, cutIcon } from './icons.js';
|
|
4
|
+
/**
|
|
5
|
+
* Copies an element to the system clipboard, as HTML and plain text. With `cut: true`, it also removes the element.
|
|
6
|
+
*
|
|
7
|
+
* The element is the given one, the parent of the given position, or the selected element or the parent of the
|
|
8
|
+
* selection (see {@link ElementOrPositionCommand}). Parts of a structure, such as table cells, rows and captions, cannot
|
|
9
|
+
* be copied on their own, so the command is disabled for them.
|
|
10
|
+
*
|
|
11
|
+
* The content goes through the clipboard pipeline, as with Ctrl+C: other plugins can change it (they listen to the
|
|
12
|
+
* `outputTransformation` event of `ClipboardPipeline`, and to `clipboardOutput` of the editing view document).
|
|
13
|
+
*
|
|
14
|
+
* Requires a secure context (HTTPS or `localhost`), as it uses the asynchronous Clipboard API.
|
|
15
|
+
*/
|
|
16
|
+
export default class ElementCopyCommand extends ElementOrPositionCommand {
|
|
17
|
+
/**
|
|
18
|
+
* Whether the element is removed after copying.
|
|
19
|
+
*/
|
|
20
|
+
cut;
|
|
21
|
+
constructor(editor, { cut = false } = {}) {
|
|
22
|
+
super(editor);
|
|
23
|
+
this.cut = cut;
|
|
24
|
+
this.title = cut ? editor.t('Cut') : editor.t('Copy');
|
|
25
|
+
this.icon = cut ? cutIcon : copyIcon;
|
|
26
|
+
// Copying does not change the data, so it stays enabled in read-only mode.
|
|
27
|
+
this.affectsData = cut;
|
|
28
|
+
}
|
|
29
|
+
_isEnabledFor(elementOrPosition) {
|
|
30
|
+
const element = this._getElement(elementOrPosition);
|
|
31
|
+
return window.isSecureContext && !!element && isStandalone(this.editor.model.schema, element);
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Copies the element (and removes it, with `cut: true`). Resolves when the clipboard has been written; rejects when
|
|
35
|
+
* the browser does not allow it.
|
|
36
|
+
*/
|
|
37
|
+
async execute(elementOrPosition) {
|
|
38
|
+
const editor = this.editor;
|
|
39
|
+
const model = editor.model;
|
|
40
|
+
const element = this._getElement(elementOrPosition);
|
|
41
|
+
if (!element || !isStandalone(model.schema, element)) {
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
const dataTransfer = new ViewDataTransfer(new DataTransfer());
|
|
45
|
+
// Always "copy": the pipeline would remove the content of the document selection when cutting.
|
|
46
|
+
editor.plugins.get(ClipboardPipeline).fire('outputTransformation', {
|
|
47
|
+
dataTransfer,
|
|
48
|
+
content: model.getSelectedContent(model.createSelection(element, 'on')),
|
|
49
|
+
method: 'copy'
|
|
50
|
+
});
|
|
51
|
+
await navigator.clipboard.write([
|
|
52
|
+
new ClipboardItem({
|
|
53
|
+
'text/html': new Blob([dataTransfer.getData('text/html')], { type: 'text/html' }),
|
|
54
|
+
'text/plain': new Blob([dataTransfer.getData('text/plain')], { type: 'text/plain' })
|
|
55
|
+
})
|
|
56
|
+
]);
|
|
57
|
+
if (this.cut && isInDocument(element) && model.canEditAt(element)) {
|
|
58
|
+
model.change(writer => {
|
|
59
|
+
writer.remove(element);
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
//# sourceMappingURL=elementCopyCommand.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"elementCopyCommand.js","sourceRoot":"","sources":["../src/elementCopyCommand.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,gBAAgB,EAAwD,MAAM,WAAW,CAAC;AACtH,OAAO,EAAE,wBAAwB,EAAE,YAAY,EAAE,YAAY,EAA0B,MAAM,yBAAyB,CAAC;AACvH,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAE/C;;;;;;;;;;;GAWG;AACH,MAAM,CAAC,OAAO,OAAO,kBAAmB,SAAQ,wBAAwB;IACvE;;OAEG;IACa,GAAG,CAAU;IAE7B,YAAY,MAAc,EAAE,EAAE,GAAG,GAAG,KAAK,KAAwB,EAAE;QAClE,KAAK,CAAC,MAAM,CAAC,CAAC;QAEd,IAAI,CAAC,GAAG,GAAG,GAAG,CAAC;QACf,IAAI,CAAC,KAAK,GAAG,GAAG,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC;QACtD,IAAI,CAAC,IAAI,GAAG,GAAG,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,QAAQ,CAAC;QAErC,2EAA2E;QAC3E,IAAI,CAAC,WAAW,GAAG,GAAG,CAAC;IACxB,CAAC;IAEkB,aAAa,CAAC,iBAAqC;QACrE,MAAM,OAAO,GAAG,IAAI,CAAC,WAAW,CAAC,iBAAiB,CAAC,CAAC;QAEpD,OAAO,MAAM,CAAC,eAAe,IAAI,CAAC,CAAC,OAAO,IAAI,YAAY,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC/F,CAAC;IAED;;;OAGG;IACa,KAAK,CAAC,OAAO,CAAC,iBAAqC;QAClE,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC;QAC3B,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC;QAC3B,MAAM,OAAO,GAAG,IAAI,CAAC,WAAW,CAAC,iBAAiB,CAAC,CAAC;QAEpD,IAAI,CAAC,OAAO,IAAI,CAAC,YAAY,CAAC,KAAK,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAC;YACtD,OAAO;QACR,CAAC;QAED,MAAM,YAAY,GAAG,IAAI,gBAAgB,CAAC,IAAI,YAAY,EAAE,CAAC,CAAC;QAE9D,+FAA+F;QAC/F,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,iBAAiB,CAAC,CAAC,IAAI,CAAqC,sBAAsB,EAAE;YACtG,YAAY;YACZ,OAAO,EAAE,KAAK,CAAC,kBAAkB,CAAC,KAAK,CAAC,eAAe,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;YACvE,MAAM,EAAE,MAAM;SACd,CAAC,CAAC;QAEH,MAAM,SAAS,CAAC,SAAS,CAAC,KAAK,CAAC;YAC/B,IAAI,aAAa,CAAC;gBACjB,WAAW,EAAE,IAAI,IAAI,CAAC,CAAC,YAAY,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC,EAAE,EAAE,IAAI,EAAE,WAAW,EAAE,CAAC;gBACjF,YAAY,EAAE,IAAI,IAAI,CAAC,CAAC,YAAY,CAAC,OAAO,CAAC,YAAY,CAAC,CAAC,EAAE,EAAE,IAAI,EAAE,YAAY,EAAE,CAAC;aACpF,CAAC;SACF,CAAC,CAAC;QAEH,IAAI,IAAI,CAAC,GAAG,IAAI,YAAY,CAAC,OAAO,CAAC,IAAI,KAAK,CAAC,SAAS,CAAC,OAAO,CAAC,EAAE,CAAC;YACnE,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE;gBACrB,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;YACxB,CAAC,CAAC,CAAC;QACJ,CAAC;IACF,CAAC;CACD"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { type Editor } from 'ckeditor5';
|
|
2
|
+
import { ElementOrPositionCommand, type ElementOrPosition } from 'ckeditor5-elements-core';
|
|
3
|
+
/**
|
|
4
|
+
* Where the pasted content goes, relative to the element:
|
|
5
|
+
*
|
|
6
|
+
* * `'replace'` – replaces the element,
|
|
7
|
+
* * `'before'` – before the element,
|
|
8
|
+
* * `'after'` – after the element.
|
|
9
|
+
*/
|
|
10
|
+
export type ElementPastePlacement = 'replace' | 'before' | 'after';
|
|
11
|
+
/**
|
|
12
|
+
* Pastes the system clipboard content relative to an element.
|
|
13
|
+
*
|
|
14
|
+
* The element is the given one, the parent of the given position, or the selected element or the parent of the
|
|
15
|
+
* selection (see {@link ElementOrPositionCommand}). Parts of a structure, such as table cells, rows and captions, cannot
|
|
16
|
+
* be replaced, nor have content pasted next to them, so the command is disabled for them.
|
|
17
|
+
*
|
|
18
|
+
* The content is pasted as with Ctrl+V: it goes through the clipboard pipeline (the `clipboardInput` event of the
|
|
19
|
+
* editing view document), so other plugins process it, e.g. paste from Office cleans up content from Word, and image
|
|
20
|
+
* upload uploads the images on the clipboard. The selection is moved to the pasted content.
|
|
21
|
+
*
|
|
22
|
+
* Requires a secure context (HTTPS or `localhost`), as it uses the asynchronous Clipboard API.
|
|
23
|
+
* The browser may ask the user for permission.
|
|
24
|
+
*/
|
|
25
|
+
export default class ElementPasteCommand extends ElementOrPositionCommand {
|
|
26
|
+
/**
|
|
27
|
+
* Where the pasted content goes, relative to the element.
|
|
28
|
+
*/
|
|
29
|
+
readonly placement: ElementPastePlacement;
|
|
30
|
+
constructor(editor: Editor, placement: ElementPastePlacement);
|
|
31
|
+
protected _isEnabledFor(elementOrPosition?: ElementOrPosition): boolean;
|
|
32
|
+
/**
|
|
33
|
+
* Pastes the clipboard content relative to the element. Resolves when the content has been pasted; rejects when the
|
|
34
|
+
* browser does not allow reading the clipboard.
|
|
35
|
+
*/
|
|
36
|
+
execute(elementOrPosition?: ElementOrPosition): Promise<void>;
|
|
37
|
+
}
|
|
38
|
+
//# sourceMappingURL=elementPasteCommand.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"elementPasteCommand.d.ts","sourceRoot":"","sources":["../src/elementPasteCommand.ts"],"names":[],"mappings":"AAAA,OAAO,EAAmE,KAAK,MAAM,EAAE,MAAM,WAAW,CAAC;AACzG,OAAO,EAAE,wBAAwB,EAA8B,KAAK,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AAGvH;;;;;;GAMG;AACH,MAAM,MAAM,qBAAqB,GAAG,SAAS,GAAG,QAAQ,GAAG,OAAO,CAAC;AAEnE;;;;;;;;;;;;;GAaG;AACH,MAAM,CAAC,OAAO,OAAO,mBAAoB,SAAQ,wBAAwB;IACxE;;OAEG;IACH,SAAgB,SAAS,EAAE,qBAAqB,CAAC;gBAErC,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,qBAAqB;cAYzC,aAAa,CAAC,iBAAiB,CAAC,EAAE,iBAAiB,GAAG,OAAO;IAMhF;;;OAGG;IACmB,OAAO,CAAC,iBAAiB,CAAC,EAAE,iBAAiB,GAAG,OAAO,CAAC,IAAI,CAAC;CAgDnF"}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import { ViewDataTransfer, plainTextToHtml } from 'ckeditor5';
|
|
2
|
+
import { ElementOrPositionCommand, isInDocument, isStandalone } from 'ckeditor5-elements-core';
|
|
3
|
+
import { pasteAfterIcon, pasteBeforeIcon, pasteIcon } from './icons.js';
|
|
4
|
+
/**
|
|
5
|
+
* Pastes the system clipboard content relative to an element.
|
|
6
|
+
*
|
|
7
|
+
* The element is the given one, the parent of the given position, or the selected element or the parent of the
|
|
8
|
+
* selection (see {@link ElementOrPositionCommand}). Parts of a structure, such as table cells, rows and captions, cannot
|
|
9
|
+
* be replaced, nor have content pasted next to them, so the command is disabled for them.
|
|
10
|
+
*
|
|
11
|
+
* The content is pasted as with Ctrl+V: it goes through the clipboard pipeline (the `clipboardInput` event of the
|
|
12
|
+
* editing view document), so other plugins process it, e.g. paste from Office cleans up content from Word, and image
|
|
13
|
+
* upload uploads the images on the clipboard. The selection is moved to the pasted content.
|
|
14
|
+
*
|
|
15
|
+
* Requires a secure context (HTTPS or `localhost`), as it uses the asynchronous Clipboard API.
|
|
16
|
+
* The browser may ask the user for permission.
|
|
17
|
+
*/
|
|
18
|
+
export default class ElementPasteCommand extends ElementOrPositionCommand {
|
|
19
|
+
/**
|
|
20
|
+
* Where the pasted content goes, relative to the element.
|
|
21
|
+
*/
|
|
22
|
+
placement;
|
|
23
|
+
constructor(editor, placement) {
|
|
24
|
+
super(editor);
|
|
25
|
+
this.placement = placement;
|
|
26
|
+
this.title = {
|
|
27
|
+
replace: editor.t('Paste'),
|
|
28
|
+
before: editor.t('Paste before'),
|
|
29
|
+
after: editor.t('Paste after')
|
|
30
|
+
}[placement];
|
|
31
|
+
this.icon = { replace: pasteIcon, before: pasteBeforeIcon, after: pasteAfterIcon }[placement];
|
|
32
|
+
}
|
|
33
|
+
_isEnabledFor(elementOrPosition) {
|
|
34
|
+
const element = this._getElement(elementOrPosition);
|
|
35
|
+
return window.isSecureContext && !!element && isStandalone(this.editor.model.schema, element);
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Pastes the clipboard content relative to the element. Resolves when the content has been pasted; rejects when the
|
|
39
|
+
* browser does not allow reading the clipboard.
|
|
40
|
+
*/
|
|
41
|
+
async execute(elementOrPosition) {
|
|
42
|
+
// Resolve the element before reading the clipboard, as the selection may change in the meantime.
|
|
43
|
+
const element = this._getElement(elementOrPosition);
|
|
44
|
+
if (!element || !isStandalone(this.editor.model.schema, element)) {
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
const dataTransfer = await readClipboard();
|
|
48
|
+
if (!dataTransfer || !isInDocument(element)) {
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
const editing = this.editor.editing;
|
|
52
|
+
const model = this.editor.model;
|
|
53
|
+
const viewElement = editing.mapper.toViewElement(element);
|
|
54
|
+
if (!viewElement) {
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
model.change(writer => {
|
|
58
|
+
const target = this.placement === 'before'
|
|
59
|
+
? writer.createRange(writer.createPositionBefore(element))
|
|
60
|
+
: this.placement === 'after'
|
|
61
|
+
? writer.createRange(writer.createPositionAfter(element))
|
|
62
|
+
: writer.createRangeOn(element);
|
|
63
|
+
if (!model.canEditAt(target)) {
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
// The same data as `ClipboardObserver` fires for Ctrl+V. The pipeline pastes at the selection.
|
|
67
|
+
const data = {
|
|
68
|
+
dataTransfer,
|
|
69
|
+
content: dataTransfer.getData('text/html') || plainTextToHtmlOrEmpty(dataTransfer.getData('text/plain')),
|
|
70
|
+
method: 'paste',
|
|
71
|
+
target: viewElement,
|
|
72
|
+
targetRanges: [editing.mapper.toViewRange(target)]
|
|
73
|
+
};
|
|
74
|
+
writer.setSelection(target);
|
|
75
|
+
editing.view.document.fire('clipboardInput', data);
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Reads the system clipboard into a data transfer: HTML, plain text and images (as files).
|
|
81
|
+
*/
|
|
82
|
+
async function readClipboard() {
|
|
83
|
+
const nativeDataTransfer = new DataTransfer();
|
|
84
|
+
for (const item of await navigator.clipboard.read()) {
|
|
85
|
+
for (const type of item.types) {
|
|
86
|
+
const blob = await item.getType(type);
|
|
87
|
+
if (type === 'text/html' || type === 'text/plain') {
|
|
88
|
+
nativeDataTransfer.setData(type, await blob.text());
|
|
89
|
+
}
|
|
90
|
+
else if (type.startsWith('image/')) {
|
|
91
|
+
nativeDataTransfer.items.add(new File([blob], `image.${type.slice('image/'.length)}`, { type }));
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
return nativeDataTransfer.types.length ? new ViewDataTransfer(nativeDataTransfer, { cacheFiles: true }) : null;
|
|
96
|
+
}
|
|
97
|
+
function plainTextToHtmlOrEmpty(text) {
|
|
98
|
+
return text ? plainTextToHtml(text) : '';
|
|
99
|
+
}
|
|
100
|
+
//# sourceMappingURL=elementPasteCommand.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"elementPasteCommand.js","sourceRoot":"","sources":["../src/elementPasteCommand.ts"],"names":[],"mappings":"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"}
|
package/dist/icons.d.ts
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The icons of the clipboard commands, in the style of the CKEditor 5 icons (20×20).
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* Copy: CKEditor's own copy icon.
|
|
6
|
+
*/
|
|
7
|
+
export declare const copyIcon: string;
|
|
8
|
+
/**
|
|
9
|
+
* Cut: scissors.
|
|
10
|
+
*/
|
|
11
|
+
export declare const cutIcon: string;
|
|
12
|
+
/**
|
|
13
|
+
* Paste: a clipboard.
|
|
14
|
+
*/
|
|
15
|
+
export declare const pasteIcon: string;
|
|
16
|
+
/**
|
|
17
|
+
* Paste before: a small clipboard above a block.
|
|
18
|
+
*/
|
|
19
|
+
export declare const pasteBeforeIcon: string;
|
|
20
|
+
/**
|
|
21
|
+
* Paste after: a block above a small clipboard.
|
|
22
|
+
*/
|
|
23
|
+
export declare const pasteAfterIcon: string;
|
|
24
|
+
//# sourceMappingURL=icons.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"icons.d.ts","sourceRoot":"","sources":["../src/icons.ts"],"names":[],"mappings":"AAEA;;GAEG;AAEH;;GAEG;AACH,eAAO,MAAM,QAAQ,QAAW,CAAC;AAEjC;;GAEG;AACH,eAAO,MAAM,OAAO,QAGiG,CAAC;AAEtH;;GAEG;AACH,eAAO,MAAM,SAAS,QAIa,CAAC;AAEpC;;GAEG;AACH,eAAO,MAAM,eAAe,QAGuE,CAAC;AAEpG;;GAEG;AACH,eAAO,MAAM,cAAc,QAG0E,CAAC"}
|
package/dist/icons.js
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { IconCopy } from 'ckeditor5';
|
|
2
|
+
/**
|
|
3
|
+
* The icons of the clipboard commands, in the style of the CKEditor 5 icons (20×20).
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* Copy: CKEditor's own copy icon.
|
|
7
|
+
*/
|
|
8
|
+
export const copyIcon = IconCopy;
|
|
9
|
+
/**
|
|
10
|
+
* Cut: scissors.
|
|
11
|
+
*/
|
|
12
|
+
export const cutIcon = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20"><path fill-rule="evenodd" d="' +
|
|
13
|
+
'M5.3 2.9 6.6 2.1l6.2 10.3a3 3 0 1 1-1.3.8L10 10.7 8.5 13.2a3 3 0 1 1-1.3-.8l2-3.3zm7.4 11.1a1.5 1.5 0 1 0 2.1 2.1 ' +
|
|
14
|
+
'1.5 1.5 0 0 0-2.1-2.1zm-7.4 0a1.5 1.5 0 1 0 2.1 2.1 1.5 1.5 0 0 0-2.1-2.1zm5.4-5.3 3.9-6.6 1.3.8-4.4 7.3z"/></svg>';
|
|
15
|
+
/**
|
|
16
|
+
* Paste: a clipboard.
|
|
17
|
+
*/
|
|
18
|
+
export const pasteIcon = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20"><path fill-rule="evenodd" d="' +
|
|
19
|
+
'M7.5 2h5a1 1 0 0 1 1 1v.5h1.7a1.3 1.3 0 0 1 1.3 1.3v11.9a1.3 1.3 0 0 1-1.3 1.3H4.8a1.3 1.3 0 0 1-1.3-1.3V4.8a1.3 ' +
|
|
20
|
+
'1.3 0 0 1 1.3-1.3h1.7V3a1 1 0 0 1 1-1zM8 3.5V5h4V3.5zM5 5v11.5h10V5h-1.5v.5a1 1 0 0 1-1 1h-5a1 1 0 0 1-1-1V5zm2 ' +
|
|
21
|
+
'4h6v1.5H7zm0 3h4v1.5H7z"/></svg>';
|
|
22
|
+
/**
|
|
23
|
+
* Paste before: a small clipboard above a block.
|
|
24
|
+
*/
|
|
25
|
+
export const pasteBeforeIcon = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20"><path fill-rule="evenodd" d="' +
|
|
26
|
+
'M8.5 1.5h3v1h1.4a.6.6 0 0 1 .6.6v5.3a.6.6 0 0 1-.6.6H7.1a.6.6 0 0 1-.6-.6V3.1a.6.6 0 0 1 .6-.6h1.4zM8 4v3.5h4V4zM3.5 ' +
|
|
27
|
+
'11h13a1 1 0 0 1 1 1v5a1 1 0 0 1-1 1h-13a1 1 0 0 1-1-1v-5a1 1 0 0 1 1-1zm.5 1.5v4h12v-4z"/></svg>';
|
|
28
|
+
/**
|
|
29
|
+
* Paste after: a block above a small clipboard.
|
|
30
|
+
*/
|
|
31
|
+
export const pasteAfterIcon = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20"><path fill-rule="evenodd" d="' +
|
|
32
|
+
'M3.5 2h13a1 1 0 0 1 1 1v5a1 1 0 0 1-1 1h-13a1 1 0 0 1-1-1V3a1 1 0 0 1 1-1zm.5 1.5v4h12v-4zM8.5 10.5h3v1h1.4a.6.6 0 0 ' +
|
|
33
|
+
'1 .6.6v5.3a.6.6 0 0 1-.6.6H7.1a.6.6 0 0 1-.6-.6v-5.3a.6.6 0 0 1 .6-.6h1.4zM8 13v3.5h4V13z"/></svg>';
|
|
34
|
+
//# sourceMappingURL=icons.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"icons.js","sourceRoot":"","sources":["../src/icons.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AAErC;;GAEG;AAEH;;GAEG;AACH,MAAM,CAAC,MAAM,QAAQ,GAAG,QAAQ,CAAC;AAEjC;;GAEG;AACH,MAAM,CAAC,MAAM,OAAO,GACnB,2FAA2F;IAC3F,oHAAoH;IACpH,oHAAoH,CAAC;AAEtH;;GAEG;AACH,MAAM,CAAC,MAAM,SAAS,GACrB,2FAA2F;IAC3F,mHAAmH;IACnH,kHAAkH;IAClH,kCAAkC,CAAC;AAEpC;;GAEG;AACH,MAAM,CAAC,MAAM,eAAe,GAC3B,2FAA2F;IAC3F,uHAAuH;IACvH,kGAAkG,CAAC;AAEpG;;GAEG;AACH,MAAM,CAAC,MAAM,cAAc,GAC1B,2FAA2F;IAC3F,uHAAuH;IACvH,oGAAoG,CAAC"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Clipboard commands that act on an element: copy, cut, paste, paste before and paste after.
|
|
3
|
+
*
|
|
4
|
+
* @module ckeditor5-elements-clipboard
|
|
5
|
+
*/
|
|
6
|
+
export { default as ElementClipboard } from './elementClipboard.js';
|
|
7
|
+
export { default as ElementCopyCommand } from './elementCopyCommand.js';
|
|
8
|
+
export { default as ElementPasteCommand, type ElementPastePlacement } from './elementPasteCommand.js';
|
|
9
|
+
export { copyIcon, cutIcon, pasteAfterIcon, pasteBeforeIcon, pasteIcon } from './icons.js';
|
|
10
|
+
import './augmentation.js';
|
|
11
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,OAAO,EAAE,OAAO,IAAI,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAGpE,OAAO,EAAE,OAAO,IAAI,kBAAkB,EAAE,MAAM,yBAAyB,CAAC;AACxE,OAAO,EAAE,OAAO,IAAI,mBAAmB,EAAE,KAAK,qBAAqB,EAAE,MAAM,0BAA0B,CAAC;AAGtG,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,cAAc,EAAE,eAAe,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAE3F,OAAO,mBAAmB,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Clipboard commands that act on an element: copy, cut, paste, paste before and paste after.
|
|
3
|
+
*
|
|
4
|
+
* @module ckeditor5-elements-clipboard
|
|
5
|
+
*/
|
|
6
|
+
// Plugins.
|
|
7
|
+
export { default as ElementClipboard } from './elementClipboard.js';
|
|
8
|
+
// Commands.
|
|
9
|
+
export { default as ElementCopyCommand } from './elementCopyCommand.js';
|
|
10
|
+
export { default as ElementPasteCommand } from './elementPasteCommand.js';
|
|
11
|
+
// Icons.
|
|
12
|
+
export { copyIcon, cutIcon, pasteAfterIcon, pasteBeforeIcon, pasteIcon } from './icons.js';
|
|
13
|
+
import './augmentation.js';
|
|
14
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,WAAW;AACX,OAAO,EAAE,OAAO,IAAI,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAEpE,YAAY;AACZ,OAAO,EAAE,OAAO,IAAI,kBAAkB,EAAE,MAAM,yBAAyB,CAAC;AACxE,OAAO,EAAE,OAAO,IAAI,mBAAmB,EAA8B,MAAM,0BAA0B,CAAC;AAEtG,SAAS;AACT,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,cAAc,EAAE,eAAe,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAE3F,OAAO,mBAAmB,CAAC"}
|
package/dist/utils.d.ts
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { ModelElement, ModelSchema } from 'ckeditor5';
|
|
2
|
+
/**
|
|
3
|
+
* Whether the element can be copied, cut or pasted over as a whole. Parts of a structure that cannot stand alone, such
|
|
4
|
+
* as a table cell, a table row or a caption, cannot: in the schema, they are limit elements that are not objects.
|
|
5
|
+
*/
|
|
6
|
+
export declare function isStandalone(schema: ModelSchema, element: ModelElement): boolean;
|
|
7
|
+
//# sourceMappingURL=utils.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AAE3D;;;GAGG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE,WAAW,EAAE,OAAO,EAAE,YAAY,GAAG,OAAO,CAEhF"}
|
package/dist/utils.js
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Whether the element can be copied, cut or pasted over as a whole. Parts of a structure that cannot stand alone, such
|
|
3
|
+
* as a table cell, a table row or a caption, cannot: in the schema, they are limit elements that are not objects.
|
|
4
|
+
*/
|
|
5
|
+
export function isStandalone(schema, element) {
|
|
6
|
+
return !schema.isLimit(element) || schema.isObject(element);
|
|
7
|
+
}
|
|
8
|
+
//# sourceMappingURL=utils.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"utils.js","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAEA;;;GAGG;AACH,MAAM,UAAU,YAAY,CAAC,MAAmB,EAAE,OAAqB;IACtE,OAAO,CAAC,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,IAAI,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;AAC7D,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,48 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ckeditor5-elements-clipboard",
|
|
3
|
-
"version": "
|
|
4
|
-
"
|
|
5
|
-
"
|
|
3
|
+
"version": "49.0.0-alpha.2",
|
|
4
|
+
"description": "Clipboard commands for CKEditor 5 elements: copy, cut, paste, paste before and paste after an element.",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"ckeditor",
|
|
7
|
+
"ckeditor5",
|
|
8
|
+
"ckeditor 5",
|
|
9
|
+
"ckeditor5-feature",
|
|
10
|
+
"ckeditor5-plugin"
|
|
11
|
+
],
|
|
12
|
+
"license": "MIT",
|
|
13
|
+
"author": "Ivan Kildyushov",
|
|
14
|
+
"homepage": "https://ivankild.github.io/ckeditor5-elements/guide/clipboard",
|
|
15
|
+
"repository": {
|
|
16
|
+
"type": "git",
|
|
17
|
+
"url": "git+https://github.com/ivankild/ckeditor5-elements.git",
|
|
18
|
+
"directory": "packages/ckeditor5-elements-clipboard"
|
|
19
|
+
},
|
|
20
|
+
"bugs": {
|
|
21
|
+
"url": "https://github.com/ivankild/ckeditor5-elements/issues"
|
|
22
|
+
},
|
|
23
|
+
"type": "module",
|
|
24
|
+
"main": "./dist/index.js",
|
|
25
|
+
"types": "./dist/index.d.ts",
|
|
26
|
+
"exports": {
|
|
27
|
+
".": {
|
|
28
|
+
"types": "./dist/index.d.ts",
|
|
29
|
+
"import": "./dist/index.js"
|
|
30
|
+
},
|
|
31
|
+
"./package.json": "./package.json"
|
|
32
|
+
},
|
|
33
|
+
"files": [
|
|
34
|
+
"dist",
|
|
35
|
+
"src",
|
|
36
|
+
"README.md"
|
|
37
|
+
],
|
|
38
|
+
"sideEffects": false,
|
|
39
|
+
"dependencies": {
|
|
40
|
+
"ckeditor5-elements-core": "49.0.0-alpha.2"
|
|
41
|
+
},
|
|
42
|
+
"peerDependencies": {
|
|
43
|
+
"ckeditor5": "^49.0.0"
|
|
44
|
+
},
|
|
45
|
+
"scripts": {
|
|
46
|
+
"build": "tsc --build"
|
|
47
|
+
}
|
|
6
48
|
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { ElementClipboard, ElementCopyCommand, ElementPasteCommand } from './index.js';
|
|
2
|
+
|
|
3
|
+
declare module 'ckeditor5' {
|
|
4
|
+
interface PluginsMap {
|
|
5
|
+
[ElementClipboard.pluginName]: ElementClipboard;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
interface CommandsMap {
|
|
9
|
+
copyElement: ElementCopyCommand;
|
|
10
|
+
cutElement: ElementCopyCommand;
|
|
11
|
+
pasteElement: ElementPasteCommand;
|
|
12
|
+
pasteElementBefore: ElementPasteCommand;
|
|
13
|
+
pasteElementAfter: ElementPasteCommand;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { ClipboardPipeline, Notification, Plugin } from 'ckeditor5';
|
|
2
|
+
import ElementCopyCommand from './elementCopyCommand.js';
|
|
3
|
+
import ElementPasteCommand from './elementPasteCommand.js';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Registers clipboard commands that act on an element:
|
|
7
|
+
* `copyElement`, `cutElement`, `pasteElement`, `pasteElementBefore` and `pasteElementAfter`.
|
|
8
|
+
*
|
|
9
|
+
* ```ts
|
|
10
|
+
* await editor.execute('copyElement', element);
|
|
11
|
+
* ```
|
|
12
|
+
*
|
|
13
|
+
* The commands have titles and icons, so they can be menu items (e.g. in the `elementContextMenu` config of
|
|
14
|
+
* `ElementContextMenu`) and buttons. Copying and pasting go through the clipboard pipeline, as Ctrl+C and Ctrl+V do.
|
|
15
|
+
*/
|
|
16
|
+
export default class ElementClipboard extends Plugin {
|
|
17
|
+
public static get requires(): readonly [typeof ClipboardPipeline, typeof Notification] {
|
|
18
|
+
// The buttons of the commands report failures (e.g. no clipboard permission) with notifications.
|
|
19
|
+
return [ClipboardPipeline, Notification] as const;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
public static get pluginName(): 'ElementClipboard' {
|
|
23
|
+
return 'ElementClipboard' as const;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
public init(): void {
|
|
27
|
+
const editor = this.editor;
|
|
28
|
+
|
|
29
|
+
editor.commands.add('copyElement', new ElementCopyCommand(editor));
|
|
30
|
+
editor.commands.add('cutElement', new ElementCopyCommand(editor, { cut: true }));
|
|
31
|
+
editor.commands.add('pasteElement', new ElementPasteCommand(editor, 'replace'));
|
|
32
|
+
editor.commands.add('pasteElementBefore', new ElementPasteCommand(editor, 'before'));
|
|
33
|
+
editor.commands.add('pasteElementAfter', new ElementPasteCommand(editor, 'after'));
|
|
34
|
+
}
|
|
35
|
+
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { ClipboardPipeline, ViewDataTransfer, type ClipboardOutputTransformationEvent, type Editor } from 'ckeditor5';
|
|
2
|
+
import { ElementOrPositionCommand, isInDocument, isStandalone, type ElementOrPosition } from 'ckeditor5-elements-core';
|
|
3
|
+
import { copyIcon, cutIcon } from './icons.js';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Copies an element to the system clipboard, as HTML and plain text. With `cut: true`, it also removes the element.
|
|
7
|
+
*
|
|
8
|
+
* The element is the given one, the parent of the given position, or the selected element or the parent of the
|
|
9
|
+
* selection (see {@link ElementOrPositionCommand}). Parts of a structure, such as table cells, rows and captions, cannot
|
|
10
|
+
* be copied on their own, so the command is disabled for them.
|
|
11
|
+
*
|
|
12
|
+
* The content goes through the clipboard pipeline, as with Ctrl+C: other plugins can change it (they listen to the
|
|
13
|
+
* `outputTransformation` event of `ClipboardPipeline`, and to `clipboardOutput` of the editing view document).
|
|
14
|
+
*
|
|
15
|
+
* Requires a secure context (HTTPS or `localhost`), as it uses the asynchronous Clipboard API.
|
|
16
|
+
*/
|
|
17
|
+
export default class ElementCopyCommand extends ElementOrPositionCommand {
|
|
18
|
+
/**
|
|
19
|
+
* Whether the element is removed after copying.
|
|
20
|
+
*/
|
|
21
|
+
public readonly cut: boolean;
|
|
22
|
+
|
|
23
|
+
constructor(editor: Editor, { cut = false }: { cut?: boolean } = {}) {
|
|
24
|
+
super(editor);
|
|
25
|
+
|
|
26
|
+
this.cut = cut;
|
|
27
|
+
this.title = cut ? editor.t('Cut') : editor.t('Copy');
|
|
28
|
+
this.icon = cut ? cutIcon : copyIcon;
|
|
29
|
+
|
|
30
|
+
// Copying does not change the data, so it stays enabled in read-only mode.
|
|
31
|
+
this.affectsData = cut;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
protected override _isEnabledFor(elementOrPosition?: ElementOrPosition): boolean {
|
|
35
|
+
const element = this._getElement(elementOrPosition);
|
|
36
|
+
|
|
37
|
+
return window.isSecureContext && !!element && isStandalone(this.editor.model.schema, element);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Copies the element (and removes it, with `cut: true`). Resolves when the clipboard has been written; rejects when
|
|
42
|
+
* the browser does not allow it.
|
|
43
|
+
*/
|
|
44
|
+
public override async execute(elementOrPosition?: ElementOrPosition): Promise<void> {
|
|
45
|
+
const editor = this.editor;
|
|
46
|
+
const model = editor.model;
|
|
47
|
+
const element = this._getElement(elementOrPosition);
|
|
48
|
+
|
|
49
|
+
if (!element || !isStandalone(model.schema, element)) {
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const dataTransfer = new ViewDataTransfer(new DataTransfer());
|
|
54
|
+
|
|
55
|
+
// Always "copy": the pipeline would remove the content of the document selection when cutting.
|
|
56
|
+
editor.plugins.get(ClipboardPipeline).fire<ClipboardOutputTransformationEvent>('outputTransformation', {
|
|
57
|
+
dataTransfer,
|
|
58
|
+
content: model.getSelectedContent(model.createSelection(element, 'on')),
|
|
59
|
+
method: 'copy'
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
await navigator.clipboard.write([
|
|
63
|
+
new ClipboardItem({
|
|
64
|
+
'text/html': new Blob([dataTransfer.getData('text/html')], { type: 'text/html' }),
|
|
65
|
+
'text/plain': new Blob([dataTransfer.getData('text/plain')], { type: 'text/plain' })
|
|
66
|
+
})
|
|
67
|
+
]);
|
|
68
|
+
|
|
69
|
+
if (this.cut && isInDocument(element) && model.canEditAt(element)) {
|
|
70
|
+
model.change(writer => {
|
|
71
|
+
writer.remove(element);
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import { ViewDataTransfer, plainTextToHtml, type ClipboardInputEventData, type Editor } from 'ckeditor5';
|
|
2
|
+
import { ElementOrPositionCommand, isInDocument, isStandalone, type ElementOrPosition } from 'ckeditor5-elements-core';
|
|
3
|
+
import { pasteAfterIcon, pasteBeforeIcon, pasteIcon } from './icons.js';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Where the pasted content goes, relative to the element:
|
|
7
|
+
*
|
|
8
|
+
* * `'replace'` – replaces the element,
|
|
9
|
+
* * `'before'` – before the element,
|
|
10
|
+
* * `'after'` – after the element.
|
|
11
|
+
*/
|
|
12
|
+
export type ElementPastePlacement = 'replace' | 'before' | 'after';
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Pastes the system clipboard content relative to an element.
|
|
16
|
+
*
|
|
17
|
+
* The element is the given one, the parent of the given position, or the selected element or the parent of the
|
|
18
|
+
* selection (see {@link ElementOrPositionCommand}). Parts of a structure, such as table cells, rows and captions, cannot
|
|
19
|
+
* be replaced, nor have content pasted next to them, so the command is disabled for them.
|
|
20
|
+
*
|
|
21
|
+
* The content is pasted as with Ctrl+V: it goes through the clipboard pipeline (the `clipboardInput` event of the
|
|
22
|
+
* editing view document), so other plugins process it, e.g. paste from Office cleans up content from Word, and image
|
|
23
|
+
* upload uploads the images on the clipboard. The selection is moved to the pasted content.
|
|
24
|
+
*
|
|
25
|
+
* Requires a secure context (HTTPS or `localhost`), as it uses the asynchronous Clipboard API.
|
|
26
|
+
* The browser may ask the user for permission.
|
|
27
|
+
*/
|
|
28
|
+
export default class ElementPasteCommand extends ElementOrPositionCommand {
|
|
29
|
+
/**
|
|
30
|
+
* Where the pasted content goes, relative to the element.
|
|
31
|
+
*/
|
|
32
|
+
public readonly placement: ElementPastePlacement;
|
|
33
|
+
|
|
34
|
+
constructor(editor: Editor, placement: ElementPastePlacement) {
|
|
35
|
+
super(editor);
|
|
36
|
+
|
|
37
|
+
this.placement = placement;
|
|
38
|
+
this.title = {
|
|
39
|
+
replace: editor.t('Paste'),
|
|
40
|
+
before: editor.t('Paste before'),
|
|
41
|
+
after: editor.t('Paste after')
|
|
42
|
+
}[placement];
|
|
43
|
+
this.icon = { replace: pasteIcon, before: pasteBeforeIcon, after: pasteAfterIcon }[placement];
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
protected override _isEnabledFor(elementOrPosition?: ElementOrPosition): boolean {
|
|
47
|
+
const element = this._getElement(elementOrPosition);
|
|
48
|
+
|
|
49
|
+
return window.isSecureContext && !!element && isStandalone(this.editor.model.schema, element);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Pastes the clipboard content relative to the element. Resolves when the content has been pasted; rejects when the
|
|
54
|
+
* browser does not allow reading the clipboard.
|
|
55
|
+
*/
|
|
56
|
+
public override async execute(elementOrPosition?: ElementOrPosition): Promise<void> {
|
|
57
|
+
// Resolve the element before reading the clipboard, as the selection may change in the meantime.
|
|
58
|
+
const element = this._getElement(elementOrPosition);
|
|
59
|
+
|
|
60
|
+
if (!element || !isStandalone(this.editor.model.schema, element)) {
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const dataTransfer = await readClipboard();
|
|
65
|
+
|
|
66
|
+
if (!dataTransfer || !isInDocument(element)) {
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const editing = this.editor.editing;
|
|
71
|
+
const model = this.editor.model;
|
|
72
|
+
const viewElement = editing.mapper.toViewElement(element);
|
|
73
|
+
|
|
74
|
+
if (!viewElement) {
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
model.change(writer => {
|
|
79
|
+
const target =
|
|
80
|
+
this.placement === 'before'
|
|
81
|
+
? writer.createRange(writer.createPositionBefore(element))
|
|
82
|
+
: this.placement === 'after'
|
|
83
|
+
? writer.createRange(writer.createPositionAfter(element))
|
|
84
|
+
: writer.createRangeOn(element);
|
|
85
|
+
|
|
86
|
+
if (!model.canEditAt(target)) {
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// The same data as `ClipboardObserver` fires for Ctrl+V. The pipeline pastes at the selection.
|
|
91
|
+
const data: ClipboardInputEventData = {
|
|
92
|
+
dataTransfer,
|
|
93
|
+
content:
|
|
94
|
+
dataTransfer.getData('text/html') || plainTextToHtmlOrEmpty(dataTransfer.getData('text/plain')),
|
|
95
|
+
method: 'paste',
|
|
96
|
+
target: viewElement,
|
|
97
|
+
targetRanges: [editing.mapper.toViewRange(target)]
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
writer.setSelection(target);
|
|
101
|
+
editing.view.document.fire('clipboardInput', data);
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Reads the system clipboard into a data transfer: HTML, plain text and images (as files).
|
|
108
|
+
*/
|
|
109
|
+
async function readClipboard(): Promise<ViewDataTransfer | null> {
|
|
110
|
+
const nativeDataTransfer = new DataTransfer();
|
|
111
|
+
|
|
112
|
+
for (const item of await navigator.clipboard.read()) {
|
|
113
|
+
for (const type of item.types) {
|
|
114
|
+
const blob = await item.getType(type);
|
|
115
|
+
|
|
116
|
+
if (type === 'text/html' || type === 'text/plain') {
|
|
117
|
+
nativeDataTransfer.setData(type, await blob.text());
|
|
118
|
+
} else if (type.startsWith('image/')) {
|
|
119
|
+
nativeDataTransfer.items.add(new File([blob], `image.${type.slice('image/'.length)}`, { type }));
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
return nativeDataTransfer.types.length ? new ViewDataTransfer(nativeDataTransfer, { cacheFiles: true }) : null;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
function plainTextToHtmlOrEmpty(text: string): string {
|
|
128
|
+
return text ? plainTextToHtml(text) : '';
|
|
129
|
+
}
|
package/src/icons.ts
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { IconCopy } from 'ckeditor5';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* The icons of the clipboard commands, in the style of the CKEditor 5 icons (20×20).
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Copy: CKEditor's own copy icon.
|
|
9
|
+
*/
|
|
10
|
+
export const copyIcon = IconCopy;
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Cut: scissors.
|
|
14
|
+
*/
|
|
15
|
+
export const cutIcon =
|
|
16
|
+
'<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20"><path fill-rule="evenodd" d="' +
|
|
17
|
+
'M5.3 2.9 6.6 2.1l6.2 10.3a3 3 0 1 1-1.3.8L10 10.7 8.5 13.2a3 3 0 1 1-1.3-.8l2-3.3zm7.4 11.1a1.5 1.5 0 1 0 2.1 2.1 ' +
|
|
18
|
+
'1.5 1.5 0 0 0-2.1-2.1zm-7.4 0a1.5 1.5 0 1 0 2.1 2.1 1.5 1.5 0 0 0-2.1-2.1zm5.4-5.3 3.9-6.6 1.3.8-4.4 7.3z"/></svg>';
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Paste: a clipboard.
|
|
22
|
+
*/
|
|
23
|
+
export const pasteIcon =
|
|
24
|
+
'<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20"><path fill-rule="evenodd" d="' +
|
|
25
|
+
'M7.5 2h5a1 1 0 0 1 1 1v.5h1.7a1.3 1.3 0 0 1 1.3 1.3v11.9a1.3 1.3 0 0 1-1.3 1.3H4.8a1.3 1.3 0 0 1-1.3-1.3V4.8a1.3 ' +
|
|
26
|
+
'1.3 0 0 1 1.3-1.3h1.7V3a1 1 0 0 1 1-1zM8 3.5V5h4V3.5zM5 5v11.5h10V5h-1.5v.5a1 1 0 0 1-1 1h-5a1 1 0 0 1-1-1V5zm2 ' +
|
|
27
|
+
'4h6v1.5H7zm0 3h4v1.5H7z"/></svg>';
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Paste before: a small clipboard above a block.
|
|
31
|
+
*/
|
|
32
|
+
export const pasteBeforeIcon =
|
|
33
|
+
'<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20"><path fill-rule="evenodd" d="' +
|
|
34
|
+
'M8.5 1.5h3v1h1.4a.6.6 0 0 1 .6.6v5.3a.6.6 0 0 1-.6.6H7.1a.6.6 0 0 1-.6-.6V3.1a.6.6 0 0 1 .6-.6h1.4zM8 4v3.5h4V4zM3.5 ' +
|
|
35
|
+
'11h13a1 1 0 0 1 1 1v5a1 1 0 0 1-1 1h-13a1 1 0 0 1-1-1v-5a1 1 0 0 1 1-1zm.5 1.5v4h12v-4z"/></svg>';
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Paste after: a block above a small clipboard.
|
|
39
|
+
*/
|
|
40
|
+
export const pasteAfterIcon =
|
|
41
|
+
'<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20"><path fill-rule="evenodd" d="' +
|
|
42
|
+
'M3.5 2h13a1 1 0 0 1 1 1v5a1 1 0 0 1-1 1h-13a1 1 0 0 1-1-1V3a1 1 0 0 1 1-1zm.5 1.5v4h12v-4zM8.5 10.5h3v1h1.4a.6.6 0 0 ' +
|
|
43
|
+
'1 .6.6v5.3a.6.6 0 0 1-.6.6H7.1a.6.6 0 0 1-.6-.6v-5.3a.6.6 0 0 1 .6-.6h1.4zM8 13v3.5h4V13z"/></svg>';
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Clipboard commands that act on an element: copy, cut, paste, paste before and paste after.
|
|
3
|
+
*
|
|
4
|
+
* @module ckeditor5-elements-clipboard
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
// Plugins.
|
|
8
|
+
export { default as ElementClipboard } from './elementClipboard.js';
|
|
9
|
+
|
|
10
|
+
// Commands.
|
|
11
|
+
export { default as ElementCopyCommand } from './elementCopyCommand.js';
|
|
12
|
+
export { default as ElementPasteCommand, type ElementPastePlacement } from './elementPasteCommand.js';
|
|
13
|
+
|
|
14
|
+
// Icons.
|
|
15
|
+
export { copyIcon, cutIcon, pasteAfterIcon, pasteBeforeIcon, pasteIcon } from './icons.js';
|
|
16
|
+
|
|
17
|
+
import './augmentation.js';
|