@ckeditor/ckeditor5-clipboard 37.1.0 → 38.0.0-rc.0

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ckeditor/ckeditor5-clipboard",
3
- "version": "37.1.0",
3
+ "version": "38.0.0-rc.0",
4
4
  "description": "Clipboard integration feature for CKEditor 5.",
5
5
  "keywords": [
6
6
  "ckeditor",
@@ -12,30 +12,48 @@
12
12
  ],
13
13
  "main": "src/index.js",
14
14
  "dependencies": {
15
- "@ckeditor/ckeditor5-core": "^37.1.0",
16
- "@ckeditor/ckeditor5-engine": "^37.1.0",
17
- "@ckeditor/ckeditor5-utils": "^37.1.0",
18
- "@ckeditor/ckeditor5-widget": "^37.1.0",
15
+ "@ckeditor/ckeditor5-core": "^38.0.0-rc.0",
16
+ "@ckeditor/ckeditor5-engine": "^38.0.0-rc.0",
17
+ "@ckeditor/ckeditor5-ui": "^38.0.0-rc.0",
18
+ "@ckeditor/ckeditor5-utils": "^38.0.0-rc.0",
19
+ "@ckeditor/ckeditor5-widget": "^38.0.0-rc.0",
19
20
  "lodash-es": "^4.17.15"
20
21
  },
21
22
  "devDependencies": {
22
- "@ckeditor/ckeditor5-alignment": "^37.1.0",
23
- "@ckeditor/ckeditor5-basic-styles": "^37.1.0",
24
- "@ckeditor/ckeditor5-block-quote": "^37.1.0",
25
- "@ckeditor/ckeditor5-cloud-services": "^37.1.0",
26
- "@ckeditor/ckeditor5-code-block": "^37.1.0",
27
- "@ckeditor/ckeditor5-easy-image": "^37.1.0",
28
- "@ckeditor/ckeditor5-editor-classic": "^37.1.0",
29
- "@ckeditor/ckeditor5-enter": "^37.1.0",
30
- "@ckeditor/ckeditor5-horizontal-line": "^37.1.0",
31
- "@ckeditor/ckeditor5-image": "^37.1.0",
32
- "@ckeditor/ckeditor5-link": "^37.1.0",
33
- "@ckeditor/ckeditor5-page-break": "^37.1.0",
34
- "@ckeditor/ckeditor5-paragraph": "^37.1.0",
35
- "@ckeditor/ckeditor5-paste-from-office": "^37.1.0",
36
- "@ckeditor/ckeditor5-remove-format": "^37.1.0",
37
- "@ckeditor/ckeditor5-table": "^37.1.0",
38
- "@ckeditor/ckeditor5-typing": "^37.1.0",
23
+ "@ckeditor/ckeditor5-alignment": "^38.0.0-rc.0",
24
+ "@ckeditor/ckeditor5-autoformat": "^38.0.0-rc.0",
25
+ "@ckeditor/ckeditor5-basic-styles": "^38.0.0-rc.0",
26
+ "@ckeditor/ckeditor5-block-quote": "^38.0.0-rc.0",
27
+ "@ckeditor/ckeditor5-cloud-services": "^38.0.0-rc.0",
28
+ "@ckeditor/ckeditor5-code-block": "^38.0.0-rc.0",
29
+ "@ckeditor/ckeditor5-easy-image": "^38.0.0-rc.0",
30
+ "@ckeditor/ckeditor5-editor-balloon": "^38.0.0-rc.0",
31
+ "@ckeditor/ckeditor5-editor-classic": "^38.0.0-rc.0",
32
+ "@ckeditor/ckeditor5-editor-decoupled": "^38.0.0-rc.0",
33
+ "@ckeditor/ckeditor5-enter": "^38.0.0-rc.0",
34
+ "@ckeditor/ckeditor5-essentials": "^38.0.0-rc.0",
35
+ "@ckeditor/ckeditor5-find-and-replace": "^38.0.0-rc.0",
36
+ "@ckeditor/ckeditor5-font": "^38.0.0-rc.0",
37
+ "@ckeditor/ckeditor5-heading": "^38.0.0-rc.0",
38
+ "@ckeditor/ckeditor5-highlight": "^38.0.0-rc.0",
39
+ "@ckeditor/ckeditor5-horizontal-line": "^38.0.0-rc.0",
40
+ "@ckeditor/ckeditor5-html-embed": "^38.0.0-rc.0",
41
+ "@ckeditor/ckeditor5-html-support": "^38.0.0-rc.0",
42
+ "@ckeditor/ckeditor5-image": "^38.0.0-rc.0",
43
+ "@ckeditor/ckeditor5-indent": "^38.0.0-rc.0",
44
+ "@ckeditor/ckeditor5-language": "^38.0.0-rc.0",
45
+ "@ckeditor/ckeditor5-link": "^38.0.0-rc.0",
46
+ "@ckeditor/ckeditor5-list": "^38.0.0-rc.0",
47
+ "@ckeditor/ckeditor5-mention": "^38.0.0-rc.0",
48
+ "@ckeditor/ckeditor5-page-break": "^38.0.0-rc.0",
49
+ "@ckeditor/ckeditor5-paragraph": "^38.0.0-rc.0",
50
+ "@ckeditor/ckeditor5-paste-from-office": "^38.0.0-rc.0",
51
+ "@ckeditor/ckeditor5-remove-format": "^38.0.0-rc.0",
52
+ "@ckeditor/ckeditor5-source-editing": "^38.0.0-rc.0",
53
+ "@ckeditor/ckeditor5-style": "^38.0.0-rc.0",
54
+ "@ckeditor/ckeditor5-table": "^38.0.0-rc.0",
55
+ "@ckeditor/ckeditor5-typing": "^38.0.0-rc.0",
56
+ "@ckeditor/ckeditor5-undo": "^38.0.0-rc.0",
39
57
  "typescript": "^4.8.4",
40
58
  "webpack": "^5.58.1",
41
59
  "webpack-cli": "^4.9.0"
@@ -2,12 +2,15 @@
2
2
  * @license Copyright (c) 2003-2023, CKSource Holding sp. z o.o. All rights reserved.
3
3
  * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
4
4
  */
5
- import type { Clipboard, ClipboardPipeline, DragDrop, PastePlainText } from './index';
5
+ import type { Clipboard, ClipboardPipeline, PastePlainText, DragDrop, DragDropExperimental, DragDropTarget, DragDropBlockToolbar } from './index';
6
6
  declare module '@ckeditor/ckeditor5-core' {
7
7
  interface PluginsMap {
8
8
  [Clipboard.pluginName]: Clipboard;
9
9
  [ClipboardPipeline.pluginName]: ClipboardPipeline;
10
- [DragDrop.pluginName]: DragDrop;
11
10
  [PastePlainText.pluginName]: PastePlainText;
11
+ [DragDrop.pluginName]: DragDrop;
12
+ [DragDropExperimental.pluginName]: DragDropExperimental;
13
+ [DragDropTarget.pluginName]: DragDropTarget;
14
+ [DragDropBlockToolbar.pluginName]: DragDropBlockToolbar;
12
15
  }
13
16
  }
@@ -48,7 +48,8 @@ export default class ClipboardObserver extends DomEventObserver {
48
48
  dataTransfer: data.dataTransfer,
49
49
  method: evt.name,
50
50
  targetRanges,
51
- target: data.target
51
+ target: data.target,
52
+ domEvent: data.domEvent
52
53
  });
53
54
  // If CKEditor handled the input, do not bubble the original event any further.
54
55
  // This helps external integrations recognize that fact and act accordingly.
@@ -136,10 +136,10 @@ export default class ClipboardPipeline extends Plugin {
136
136
  const model = editor.model;
137
137
  const view = editor.editing.view;
138
138
  const viewDocument = view.document;
139
- // Pasting and dropping is disabled when editor is in the read-only mode.
140
- // See: https://github.com/ckeditor/ckeditor5-clipboard/issues/26.
141
- this.listenTo(viewDocument, 'clipboardInput', evt => {
142
- if (editor.isReadOnly) {
139
+ // Pasting is disabled when selection is in non-editable place.
140
+ // Dropping is disabled in drag and drop handler.
141
+ this.listenTo(viewDocument, 'clipboardInput', (evt, data) => {
142
+ if (data.method == 'paste' && !editor.model.canEditAt(editor.model.document.selection)) {
143
143
  evt.stop();
144
144
  }
145
145
  }, { priority: 'highest' });
@@ -223,9 +223,9 @@ export default class ClipboardPipeline extends Plugin {
223
223
  };
224
224
  this.listenTo(viewDocument, 'copy', onCopyCut, { priority: 'low' });
225
225
  this.listenTo(viewDocument, 'cut', (evt, data) => {
226
- // Cutting is disabled when editor is in the read-only mode.
226
+ // Cutting is disabled when selection is in non-editable place.
227
227
  // See: https://github.com/ckeditor/ckeditor5-clipboard/issues/26.
228
- if (editor.isReadOnly) {
228
+ if (!editor.model.canEditAt(editor.model.document.selection)) {
229
229
  data.preventDefault();
230
230
  }
231
231
  else {
package/src/dragdrop.js CHANGED
@@ -5,11 +5,10 @@
5
5
  /**
6
6
  * @module clipboard/dragdrop
7
7
  */
8
- /* globals setTimeout, clearTimeout */
9
8
  import { Plugin } from '@ckeditor/ckeditor5-core';
10
9
  import { LiveRange, MouseObserver } from '@ckeditor/ckeditor5-engine';
11
10
  import { Widget, isWidget } from '@ckeditor/ckeditor5-widget';
12
- import { env, uid } from '@ckeditor/ckeditor5-utils';
11
+ import { env, uid, delay } from '@ckeditor/ckeditor5-utils';
13
12
  import ClipboardPipeline from './clipboardpipeline';
14
13
  import ClipboardObserver from './clipboardobserver';
15
14
  import { throttle } from 'lodash-es';
@@ -121,6 +120,10 @@ export default class DragDrop extends Plugin {
121
120
  this._updateDropMarkerThrottled = throttle(targetRange => this._updateDropMarker(targetRange), 40);
122
121
  this._removeDropMarkerDelayed = delay(() => this._removeDropMarker(), 40);
123
122
  this._clearDraggableAttributesDelayed = delay(() => this._clearDraggableAttributes(), 40);
123
+ if (editor.plugins.has('DragDropExperimental')) {
124
+ this.forceDisabled('DragDropExperimental');
125
+ return;
126
+ }
124
127
  view.addObserver(ClipboardObserver);
125
128
  view.addObserver(MouseObserver);
126
129
  this._setupDragging();
@@ -201,7 +204,8 @@ export default class DragDrop extends Plugin {
201
204
  return;
202
205
  }
203
206
  this._draggingUid = uid();
204
- data.dataTransfer.effectAllowed = this.isEnabled ? 'copyMove' : 'copy';
207
+ const canEditAtDraggedRange = this.isEnabled && editor.model.canEditAt(this._draggedRange);
208
+ data.dataTransfer.effectAllowed = canEditAtDraggedRange ? 'copyMove' : 'copy';
205
209
  data.dataTransfer.setData('application/ckeditor5-dragging-uid', this._draggingUid);
206
210
  const draggedSelection = model.createSelection(this._draggedRange.toRange());
207
211
  const content = editor.data.toView(model.getSelectedContent(draggedSelection));
@@ -210,7 +214,7 @@ export default class DragDrop extends Plugin {
210
214
  content,
211
215
  method: 'dragstart'
212
216
  });
213
- if (!this.isEnabled) {
217
+ if (!canEditAtDraggedRange) {
214
218
  this._draggedRange.detach();
215
219
  this._draggedRange = null;
216
220
  this._draggingUid = '';
@@ -243,6 +247,11 @@ export default class DragDrop extends Plugin {
243
247
  }
244
248
  this._removeDropMarkerDelayed.cancel();
245
249
  const targetRange = findDropTargetRange(editor, data.targetRanges, data.target);
250
+ // Do not drop if target place is not editable.
251
+ if (!editor.model.canEditAt(targetRange)) {
252
+ data.dataTransfer.dropEffect = 'none';
253
+ return;
254
+ }
246
255
  // If this is content being dragged from another editor, moving out of current editor instance
247
256
  // is not possible until 'dragend' event case will be fixed.
248
257
  if (!this._draggedRange) {
@@ -280,7 +289,7 @@ export default class DragDrop extends Plugin {
280
289
  // the target lands on the marker itself.
281
290
  this._removeDropMarker();
282
291
  /* istanbul ignore if -- @preserve */
283
- if (!targetRange) {
292
+ if (!targetRange || !editor.model.canEditAt(targetRange)) {
284
293
  this._finalizeDragging(false);
285
294
  evt.stop();
286
295
  return;
@@ -355,10 +364,13 @@ export default class DragDrop extends Plugin {
355
364
  // In Firefox this is not needed. In Safari it makes the whole editable draggable (not just textual content).
356
365
  // Disabled in read-only mode because draggable="true" + contenteditable="false" results
357
366
  // in not firing selectionchange event ever, which makes the selection stuck in read-only mode.
358
- if (env.isBlink && !editor.isReadOnly && !draggableElement && !viewDocument.selection.isCollapsed) {
367
+ if (env.isBlink && !draggableElement && !viewDocument.selection.isCollapsed) {
359
368
  const selectedElement = viewDocument.selection.getSelectedElement();
360
369
  if (!selectedElement || !isWidget(selectedElement)) {
361
- draggableElement = viewDocument.selection.editableElement;
370
+ const editableElement = viewDocument.selection.editableElement;
371
+ if (editableElement && !editableElement.isReadOnly) {
372
+ draggableElement = editableElement;
373
+ }
362
374
  }
363
375
  }
364
376
  if (draggableElement) {
@@ -617,23 +629,6 @@ function getFinalDropEffect(dataTransfer) {
617
629
  }
618
630
  return ['all', 'copyMove'].includes(dataTransfer.effectAllowed) ? 'move' : 'copy';
619
631
  }
620
- /**
621
- * Returns a function wrapper that will trigger a function after a specified wait time.
622
- * The timeout can be canceled by calling the cancel function on the returned wrapped function.
623
- * @param func The function to wrap.
624
- * @param wait The timeout in ms.
625
- */
626
- function delay(func, wait) {
627
- let timer;
628
- function delayed(...args) {
629
- delayed.cancel();
630
- timer = setTimeout(() => func(...args), wait);
631
- }
632
- delayed.cancel = () => {
633
- clearTimeout(timer);
634
- };
635
- return delayed;
636
- }
637
632
  /**
638
633
  * Returns a widget element that should be dragged.
639
634
  */
@@ -0,0 +1,47 @@
1
+ /**
2
+ * @license Copyright (c) 2003-2023, CKSource Holding sp. z o.o. All rights reserved.
3
+ * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
4
+ */
5
+ /**
6
+ * @module clipboard/dragdropblocktoolbar
7
+ */
8
+ import { Plugin } from '@ckeditor/ckeditor5-core';
9
+ /**
10
+ * Integration of an experimental block Drag and drop support with the block toolbar.
11
+ *
12
+ * @internal
13
+ */
14
+ export default class DragDropBlockToolbar extends Plugin {
15
+ /**
16
+ * Whether current dragging is started by block toolbar button dragging.
17
+ */
18
+ private _isBlockDragging;
19
+ /**
20
+ * DOM Emitter.
21
+ */
22
+ private _domEmitter;
23
+ /**
24
+ * @inheritDoc
25
+ */
26
+ static get pluginName(): 'DragDropBlockToolbar';
27
+ /**
28
+ * @inheritDoc
29
+ */
30
+ init(): void;
31
+ /**
32
+ * @inheritDoc
33
+ */
34
+ destroy(): void;
35
+ /**
36
+ * The `dragstart` event handler.
37
+ */
38
+ private _handleBlockDragStart;
39
+ /**
40
+ * The `dragover` and `drop` event handler.
41
+ */
42
+ private _handleBlockDragging;
43
+ /**
44
+ * The `dragend` event handler.
45
+ */
46
+ private _handleBlockDragEnd;
47
+ }
@@ -0,0 +1,114 @@
1
+ /**
2
+ * @license Copyright (c) 2003-2023, CKSource Holding sp. z o.o. All rights reserved.
3
+ * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
4
+ */
5
+ /**
6
+ * @module clipboard/dragdropblocktoolbar
7
+ */
8
+ /* istanbul ignore file -- @preserve */
9
+ import { Plugin } from '@ckeditor/ckeditor5-core';
10
+ import { env, global, DomEmitterMixin } from '@ckeditor/ckeditor5-utils';
11
+ import ClipboardObserver from './clipboardobserver';
12
+ /**
13
+ * Integration of an experimental block Drag and drop support with the block toolbar.
14
+ *
15
+ * @internal
16
+ */
17
+ export default class DragDropBlockToolbar extends Plugin {
18
+ constructor() {
19
+ super(...arguments);
20
+ /**
21
+ * Whether current dragging is started by block toolbar button dragging.
22
+ */
23
+ this._isBlockDragging = false;
24
+ /**
25
+ * DOM Emitter.
26
+ */
27
+ this._domEmitter = new (DomEmitterMixin())();
28
+ }
29
+ /**
30
+ * @inheritDoc
31
+ */
32
+ static get pluginName() {
33
+ return 'DragDropBlockToolbar';
34
+ }
35
+ /**
36
+ * @inheritDoc
37
+ */
38
+ init() {
39
+ const editor = this.editor;
40
+ this.listenTo(editor, 'change:isReadOnly', (evt, name, isReadOnly) => {
41
+ if (isReadOnly) {
42
+ this.forceDisabled('readOnlyMode');
43
+ this._isBlockDragging = false;
44
+ }
45
+ else {
46
+ this.clearForceDisabled('readOnlyMode');
47
+ }
48
+ });
49
+ if (env.isAndroid) {
50
+ this.forceDisabled('noAndroidSupport');
51
+ }
52
+ if (editor.plugins.has('BlockToolbar')) {
53
+ const blockToolbar = editor.plugins.get('BlockToolbar');
54
+ const element = blockToolbar.buttonView.element;
55
+ element.setAttribute('draggable', 'true');
56
+ this._domEmitter.listenTo(element, 'dragstart', (evt, data) => this._handleBlockDragStart(data));
57
+ this._domEmitter.listenTo(global.document, 'dragover', (evt, data) => this._handleBlockDragging(data));
58
+ this._domEmitter.listenTo(global.document, 'drop', (evt, data) => this._handleBlockDragging(data));
59
+ this._domEmitter.listenTo(global.document, 'dragend', () => this._handleBlockDragEnd(), { useCapture: true });
60
+ }
61
+ }
62
+ /**
63
+ * @inheritDoc
64
+ */
65
+ destroy() {
66
+ this._domEmitter.stopListening();
67
+ return super.destroy();
68
+ }
69
+ /**
70
+ * The `dragstart` event handler.
71
+ */
72
+ _handleBlockDragStart(domEvent) {
73
+ if (!this.isEnabled) {
74
+ return;
75
+ }
76
+ const model = this.editor.model;
77
+ const selection = model.document.selection;
78
+ const blocks = Array.from(selection.getSelectedBlocks());
79
+ const draggedRange = model.createRange(model.createPositionBefore(blocks[0]), model.createPositionAfter(blocks[blocks.length - 1]));
80
+ model.change(writer => writer.setSelection(draggedRange));
81
+ this._isBlockDragging = true;
82
+ this.editor.editing.view.getObserver(ClipboardObserver).onDomEvent(domEvent);
83
+ }
84
+ /**
85
+ * The `dragover` and `drop` event handler.
86
+ */
87
+ _handleBlockDragging(domEvent) {
88
+ if (!this.isEnabled || !this._isBlockDragging) {
89
+ return;
90
+ }
91
+ const clientX = domEvent.clientX + 100;
92
+ const clientY = domEvent.clientY;
93
+ const target = document.elementFromPoint(clientX, clientY);
94
+ if (!target || !target.closest('.ck-editor__editable')) {
95
+ return;
96
+ }
97
+ this.editor.editing.view.getObserver(ClipboardObserver).onDomEvent({
98
+ ...domEvent,
99
+ type: domEvent.type,
100
+ dataTransfer: domEvent.dataTransfer,
101
+ target,
102
+ clientX,
103
+ clientY,
104
+ preventDefault: () => domEvent.preventDefault(),
105
+ stopPropagation: () => domEvent.stopPropagation()
106
+ });
107
+ }
108
+ /**
109
+ * The `dragend` event handler.
110
+ */
111
+ _handleBlockDragEnd() {
112
+ this._isBlockDragging = false;
113
+ }
114
+ }
@@ -0,0 +1,101 @@
1
+ /**
2
+ * @license Copyright (c) 2003-2023, CKSource Holding sp. z o.o. All rights reserved.
3
+ * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
4
+ */
5
+ /**
6
+ * @module clipboard/dragdropexperimental
7
+ */
8
+ import { Plugin } from '@ckeditor/ckeditor5-core';
9
+ import { Widget } from '@ckeditor/ckeditor5-widget';
10
+ import ClipboardPipeline from './clipboardpipeline';
11
+ import DragDropTarget from './dragdroptarget';
12
+ import '../theme/clipboard.css';
13
+ /**
14
+ * The drag and drop feature. It works on top of the {@link module:clipboard/clipboardpipeline~ClipboardPipeline}.
15
+ *
16
+ * Read more about the clipboard integration in the {@glink framework/deep-dive/clipboard clipboard deep-dive} guide.
17
+ *
18
+ * @internal
19
+ */
20
+ export default class DragDropExperimental extends Plugin {
21
+ /**
22
+ * The live range over the original content that is being dragged.
23
+ */
24
+ private _draggedRange;
25
+ /**
26
+ * The UID of current dragging that is used to verify if the drop started in the same editor as the drag start.
27
+ *
28
+ * **Note**: This is a workaround for broken 'dragend' events (they are not fired if the source text node got removed).
29
+ */
30
+ private _draggingUid;
31
+ /**
32
+ * The reference to the model element that currently has a `draggable` attribute set (it is set while dragging).
33
+ */
34
+ private _draggableElement;
35
+ /**
36
+ * A delayed callback removing draggable attributes.
37
+ */
38
+ private _clearDraggableAttributesDelayed;
39
+ /**
40
+ * Whether the dragged content can be dropped only in block context.
41
+ */
42
+ private _blockMode;
43
+ /**
44
+ * DOM Emitter.
45
+ */
46
+ private _domEmitter;
47
+ /**
48
+ * The DOM element used to generate dragged preview image.
49
+ */
50
+ private _previewContainer?;
51
+ /**
52
+ * @inheritDoc
53
+ */
54
+ static get pluginName(): 'DragDropExperimental';
55
+ /**
56
+ * @inheritDoc
57
+ */
58
+ static get requires(): readonly [typeof ClipboardPipeline, typeof Widget, typeof DragDropTarget];
59
+ /**
60
+ * @inheritDoc
61
+ */
62
+ init(): void;
63
+ /**
64
+ * @inheritDoc
65
+ */
66
+ destroy(): void;
67
+ /**
68
+ * Drag and drop events handling.
69
+ */
70
+ private _setupDragging;
71
+ /**
72
+ * Integration with the `clipboardInput` event.
73
+ */
74
+ private _setupClipboardInputIntegration;
75
+ /**
76
+ * Integration with the `contentInsertion` event of the clipboard pipeline.
77
+ */
78
+ private _setupContentInsertionIntegration;
79
+ /**
80
+ * Adds listeners that add the `draggable` attribute to the elements while the mouse button is down so the dragging could start.
81
+ */
82
+ private _setupDraggableAttributeHandling;
83
+ /**
84
+ * Removes the `draggable` attribute from the element that was used for dragging.
85
+ */
86
+ private _clearDraggableAttributes;
87
+ /**
88
+ * Deletes the dragged content from its original range and clears the dragging state.
89
+ *
90
+ * @param moved Whether the move succeeded.
91
+ */
92
+ private _finalizeDragging;
93
+ /**
94
+ * Sets the dragged source range based on event target and document selection.
95
+ */
96
+ private _prepareDraggedRange;
97
+ /**
98
+ * Updates the dragged preview image.
99
+ */
100
+ private _updatePreview;
101
+ }