Skip to content
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

Handling of dragging document list items. #15030

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 20 additions & 8 deletions packages/ckeditor5-clipboard/docs/framework/deep-dive/clipboard.md
Original file line number Diff line number Diff line change
Expand Up @@ -231,15 +231,20 @@ The output pipeline is the equivalent of the input pipeline but for the copy and
```plaintext
┌──────────────────────┐ ┌──────────────────────┐ Retrieves the selected
│ view.Document │ │ view.Document │ model.DocumentFragment
│ copy │ │ cut │ and converts it to
└───────────┬──────────┘ └───────────┬──────────┘ view.DocumentFragment.
│ copy │ │ cut │ and fires `outputTransformation`
└───────────┬──────────┘ └───────────┬──────────┘ event.
│ │
└────────────────┌────────────────┘
┌─────────V────────┐ Processes view.DocumentFragment
│ view.Document │ to text/html and text/plain
│ clipboardOutput │ and stores results in data.dataTransfer.
└──────────────────┘
┌─────────────V────────────┐ Processes model.DocumentFragment
│ ClipboardPipeline │ and converts it to
│ outputTransformation │ view.DocumentFragment.
└──────────────────────────┘
┌─────────────V────────────┐ Processes view.DocumentFragment
│ view.Document │ to text/html and text/plain
│ clipboardOutput │ and stores results in data.dataTransfer.
└──────────────────────────┘
```

### 1. On {@link module:engine/view/document~Document#event:copy `view.Document#copy`} and {@link module:engine/view/document~Document#event:cut `view.Document#cut`}
Expand All @@ -248,9 +253,16 @@ The default action is to:

1. {@link module:engine/model/model~Model#getSelectedContent Get the selected content} from the editor.
1. Prevent the default action of the native `copy` or `cut` event.
1. Fire {@link module:engine/view/document~Document#event:clipboardOutput `view.Document#clipboardOutput`} with a clone of the selected content converted to a {@link module:engine/view/documentfragment~DocumentFragment view document fragment}.
1. Fire {@link module:engine/view/document~Document#event:outputTransformation `view.Document#outputTransformation`}` with a selected content represented by a {@link module:engine/model/documentfragment~DocumentFragment model document fragment}.

### 2. On {@link module:engine/view/document~Document#event:outputTransformation `view.Document#outputTransformation`}

The default action is to:

1. Processes `data.content` represented by a {@link module:engine/model/documentfragment~DocumentFragment model document fragment}.
1. Fire {@link module:engine/view/document~Document#event:clipboardOutput `view.Document#clipboardOutput`} with a processed `data.content` converted to a {@link module:engine/view/documentfragment~DocumentFragment view document fragment}.

### 2. On {@link module:engine/view/document~Document#event:clipboardOutput `view.Document#clipboardOutput`}
### 3. On {@link module:engine/view/document~Document#event:clipboardOutput `view.Document#clipboardOutput`}

The default action is to put the content (`data.content`, represented by a {@link module:engine/view/documentfragment~DocumentFragment}) to the clipboard as HTML. In case of the cut operation, the selected content is also deleted from the editor.

Expand Down
86 changes: 77 additions & 9 deletions packages/ckeditor5-clipboard/src/clipboardpipeline.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,9 @@ import type {
DomEventData,
Range,
ViewDocumentFragment,
ViewRange
ViewRange,
Selection,
DocumentSelection
} from '@ckeditor/ckeditor5-engine';

import ClipboardObserver, {
Expand Down Expand Up @@ -60,11 +62,16 @@ import viewToPlainText from './utils/viewtoplaintext';
//
// ┌──────────────────────┐ ┌──────────────────────┐
// │ view.Document │ │ view.Document │ Retrieves the selected model.DocumentFragment
// │ copy │ │ cut │ and converts it to view.DocumentFragment.
// │ copy │ │ cut │ and fires `outputTransformation` event.
// └───────────┬──────────┘ └───────────┬──────────┘
// │ │
// └────────────────┌────────────────┘
// │
// ┌───────────V───────────┐
// │ ClipboardPipeline │ Processes model.DocumentFragment and converts it to
// │ outputTransformation │ view.DocumentFragment.
// └───────────┬───────────┘
// │
// ┌─────────V────────┐
// │ view.Document │ Processes view.DocumentFragment to text/html and text/plain
// │ clipboardOutput │ and stores the results in data.dataTransfer.
Expand Down Expand Up @@ -153,6 +160,25 @@ export default class ClipboardPipeline extends Plugin {
this._setupCopyCut();
}

/**
* Fires Clipboard `'outputTransformation'` event for given parameters.
*
* @internal
*/
public _fireOutputTransformationEvent(
dataTransfer: DataTransfer,
selection: Selection | DocumentSelection,
method: 'copy' | 'cut' | 'dragstart'
): void {
const content = this.editor.model.getSelectedContent( selection );

this.fire<ClipboardOutputTransformationEvent>( 'outputTransformation', {
dataTransfer,
content,
method
} );
}

/**
* The clipboard paste pipeline.
*/
Expand Down Expand Up @@ -257,13 +283,7 @@ export default class ClipboardPipeline extends Plugin {

data.preventDefault();

const content = editor.data.toView( editor.model.getSelectedContent( modelDocument.selection ) );

viewDocument.fire<ViewDocumentClipboardOutputEvent>( 'clipboardOutput', {
dataTransfer,
content,
method: evt.name
} );
this._fireOutputTransformationEvent( dataTransfer, modelDocument.selection, evt.name );
};

this.listenTo<ViewDocumentCopyEvent>( viewDocument, 'copy', onCopyCut, { priority: 'low' } );
Expand All @@ -277,6 +297,16 @@ export default class ClipboardPipeline extends Plugin {
}
}, { priority: 'low' } );

this.listenTo<ClipboardOutputTransformationEvent>( this, 'outputTransformation', ( evt, data ) => {
const content = editor.data.toView( data.content );

viewDocument.fire<ViewDocumentClipboardOutputEvent>( 'clipboardOutput', {
dataTransfer: data.dataTransfer,
content,
method: data.method
} );
}, { priority: 'low' } );

this.listenTo<ViewDocumentClipboardOutputEvent>( viewDocument, 'clipboardOutput', ( evt, data ) => {
if ( !data.content.isEmpty ) {
data.dataTransfer.setData( 'text/html', this.editor.data.htmlProcessor.toData( data.content ) );
Expand Down Expand Up @@ -441,3 +471,41 @@ export interface ViewDocumentClipboardOutputEventData {
*/
method: 'copy' | 'cut' | 'dragstart';
}

/**
* Fired on {@link module:engine/view/document~Document#event:copy}, {@link module:engine/view/document~Document#event:cut}
* and {@link module:engine/view/document~Document#event:dragstart}. The content can be processed before it ends up in the clipboard.
*
* It is a part of the {@glink framework/deep-dive/clipboard#output-pipeline clipboard output pipeline}.
*
* @eventName ~ClipboardPipeline##outputTransformation
* @param data The event data.
*/
export type ClipboardOutputTransformationEvent = {
name: 'outputTransformation';
args: [ data: ClipboardOutputTransformationData ];
};

/**
* The value of the 'outputTransformation' event.
*/
export interface ClipboardOutputTransformationData {

/**
* The data transfer instance.
*
* @readonly
*/
dataTransfer: DataTransfer;

/**
* Content to be put into the clipboard. It can be modified by the event listeners.
* Read more about the clipboard pipelines in the {@glink framework/deep-dive/clipboard clipboard deep-dive} guide.
*/
content: DocumentFragment;

/**
* Whether the event was triggered by a copy or cut operation.
*/
method: 'copy' | 'cut' | 'dragstart';
}
11 changes: 3 additions & 8 deletions packages/ckeditor5-clipboard/src/dragdrop.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,7 @@ import {
} from '@ckeditor/ckeditor5-utils';

import ClipboardPipeline, {
type ClipboardContentInsertionEvent,
type ViewDocumentClipboardOutputEvent
type ClipboardContentInsertionEvent
} from './clipboardpipeline';

import ClipboardObserver, {
Expand Down Expand Up @@ -290,13 +289,9 @@ export default class DragDrop extends Plugin {
data.dataTransfer.setData( 'application/ckeditor5-dragging-uid', this._draggingUid );

const draggedSelection = model.createSelection( this._draggedRange.toRange() );
const content = editor.data.toView( model.getSelectedContent( draggedSelection ) );
const clipboardPipeline: ClipboardPipeline = this.editor.plugins.get( 'ClipboardPipeline' );

viewDocument.fire<ViewDocumentClipboardOutputEvent>( 'clipboardOutput', {
dataTransfer: data.dataTransfer,
content,
method: 'dragstart'
} );
clipboardPipeline._fireOutputTransformationEvent( data.dataTransfer, draggedSelection, 'dragstart' );

const { dataTransfer, domTarget, domEvent } = data;
const { clientX } = domEvent;
Expand Down
2 changes: 2 additions & 0 deletions packages/ckeditor5-clipboard/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ export {
type ClipboardContentInsertionEvent,
type ClipboardInputTransformationEvent,
type ClipboardInputTransformationData,
type ClipboardOutputTransformationEvent,
type ClipboardOutputTransformationData,
type ViewDocumentClipboardOutputEvent
} from './clipboardpipeline';

Expand Down
23 changes: 23 additions & 0 deletions packages/ckeditor5-clipboard/tests/clipboardpipeline.js
Original file line number Diff line number Diff line change
Expand Up @@ -460,6 +460,29 @@ describe( 'ClipboardPipeline feature', () => {
} );

describe( 'clipboard copy/cut pipeline', () => {
it( 'fires the outputTransformation event on the clipboardPlugin', done => {
const dataTransferMock = createDataTransfer();
const preventDefaultSpy = sinon.spy();

setModelData( editor.model, '<paragraph>a[bc</paragraph><paragraph>de]f</paragraph>' );

clipboardPlugin.on( 'outputTransformation', ( evt, data ) => {
expect( preventDefaultSpy.calledOnce ).to.be.true;

expect( data.method ).to.equal( 'copy' );
expect( data.dataTransfer ).to.equal( dataTransferMock );
expect( data.content ).is.instanceOf( ModelDocumentFragment );
expect( stringifyModel( data.content ) ).to.equal( '<paragraph>bc</paragraph><paragraph>de</paragraph>' );

done();
} );

viewDocument.fire( 'copy', {
dataTransfer: dataTransferMock,
preventDefault: preventDefaultSpy
} );
} );

it( 'fires clipboardOutput for copy with the selected content and correct method', done => {
const dataTransferMock = createDataTransfer();
const preventDefaultSpy = sinon.spy();
Expand Down
23 changes: 23 additions & 0 deletions packages/ckeditor5-clipboard/tests/manual/dragdroplists.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<h2>Classic Editor - document lists</h2>

<div id="editor-classic-lists">
<p></p>
<p></p>
<ul>
<li><strong>Creating new types of editors.</strong> You can create new editor types using the framework.</li>
<li><strong>Writing your own features.</strong> New features are implemented using the framework.</li>
</ul>
<p></p>
<ul>
<li><strong>Customizing existing features.</strong> Changing the behavior or look of existing features can be
done
thanks to the framework’s capabilities.</li>
</ul>
<p></p>
<blockquote>
<ul>
<li><a href="#">Quoted</a> UL List item 1</li>
</ul>
</blockquote>
<p></p>
</div>
107 changes: 107 additions & 0 deletions packages/ckeditor5-clipboard/tests/manual/dragdroplists.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
/**
* @license Copyright (c) 2003-2023, CKSource Holding sp. z o.o. All rights reserved.
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
*/

/* globals console, window, document */

import ClassicEditor from '@ckeditor/ckeditor5-editor-classic/src/classiceditor';
import { Essentials } from '@ckeditor/ckeditor5-essentials';
import { Autoformat } from '@ckeditor/ckeditor5-autoformat';
import { BlockQuote } from '@ckeditor/ckeditor5-block-quote';
import { Bold, Italic } from '@ckeditor/ckeditor5-basic-styles';
import { Heading } from '@ckeditor/ckeditor5-heading';
import { Image, ImageCaption, ImageStyle, ImageToolbar } from '@ckeditor/ckeditor5-image';
import { Indent } from '@ckeditor/ckeditor5-indent';
import { Link } from '@ckeditor/ckeditor5-link';
import { DocumentList, DocumentListProperties } from '@ckeditor/ckeditor5-list';
import { Paragraph } from '@ckeditor/ckeditor5-paragraph';
import { Table, TableToolbar } from '@ckeditor/ckeditor5-table';
import Underline from '@ckeditor/ckeditor5-basic-styles/src/underline';
import Strikethrough from '@ckeditor/ckeditor5-basic-styles/src/strikethrough';
import Superscript from '@ckeditor/ckeditor5-basic-styles/src/superscript';
import Subscript from '@ckeditor/ckeditor5-basic-styles/src/subscript';
import Code from '@ckeditor/ckeditor5-basic-styles/src/code';
import RemoveFormat from '@ckeditor/ckeditor5-remove-format/src/removeformat';
import FindAndReplace from '@ckeditor/ckeditor5-find-and-replace/src/findandreplace';
import FontColor from '@ckeditor/ckeditor5-font/src/fontcolor';
import FontBackgroundColor from '@ckeditor/ckeditor5-font/src/fontbackgroundcolor';
import FontFamily from '@ckeditor/ckeditor5-font/src/fontfamily';
import FontSize from '@ckeditor/ckeditor5-font/src/fontsize';
import Highlight from '@ckeditor/ckeditor5-highlight/src/highlight';
import CodeBlock from '@ckeditor/ckeditor5-code-block/src/codeblock';
import TableProperties from '@ckeditor/ckeditor5-table/src/tableproperties';
import TableCellProperties from '@ckeditor/ckeditor5-table/src/tablecellproperties';
import TableCaption from '@ckeditor/ckeditor5-table/src/tablecaption';
import TableColumnResize from '@ckeditor/ckeditor5-table/src/tablecolumnresize';
import EasyImage from '@ckeditor/ckeditor5-easy-image/src/easyimage';
import ImageResize from '@ckeditor/ckeditor5-image/src/imageresize';
import ImageInsert from '@ckeditor/ckeditor5-image/src/imageinsert';
import LinkImage from '@ckeditor/ckeditor5-link/src/linkimage';
import AutoImage from '@ckeditor/ckeditor5-image/src/autoimage';
import HtmlEmbed from '@ckeditor/ckeditor5-html-embed/src/htmlembed';
import AutoLink from '@ckeditor/ckeditor5-link/src/autolink';
import Mention from '@ckeditor/ckeditor5-mention/src/mention';
import TextTransformation from '@ckeditor/ckeditor5-typing/src/texttransformation';
import Alignment from '@ckeditor/ckeditor5-alignment/src/alignment';
import IndentBlock from '@ckeditor/ckeditor5-indent/src/indentblock';
import PageBreak from '@ckeditor/ckeditor5-page-break/src/pagebreak';
import HorizontalLine from '@ckeditor/ckeditor5-horizontal-line/src/horizontalline';
import CloudServices from '@ckeditor/ckeditor5-cloud-services/src/cloudservices';
import TextPartLanguage from '@ckeditor/ckeditor5-language/src/textpartlanguage';
import SourceEditing from '@ckeditor/ckeditor5-source-editing/src/sourceediting';
import Style from '@ckeditor/ckeditor5-style/src/style';
import GeneralHtmlSupport from '@ckeditor/ckeditor5-html-support/src/generalhtmlsupport';

import { CS_CONFIG } from '@ckeditor/ckeditor5-cloud-services/tests/_utils/cloud-services-config';

ClassicEditor
.create( document.querySelector( '#editor-classic-lists' ), {
plugins: [
Essentials, Autoformat, BlockQuote, Bold, Heading, Image, ImageCaption, ImageStyle, ImageToolbar, Indent, Italic, Link,
DocumentList, Paragraph, Table, TableToolbar, Underline, Strikethrough, Superscript, Subscript, Code, RemoveFormat,
FindAndReplace, FontColor, FontBackgroundColor, FontFamily, FontSize, Highlight,
CodeBlock, DocumentListProperties, TableProperties, TableCellProperties, TableCaption, TableColumnResize,
EasyImage, ImageResize, ImageInsert, LinkImage, AutoImage, HtmlEmbed,
AutoLink, Mention, TextTransformation, Alignment, IndentBlock, PageBreak, HorizontalLine,
CloudServices, TextPartLanguage, SourceEditing, Style, GeneralHtmlSupport
],
toolbar: [
'heading', 'style',
'|',
'removeFormat', 'bold', 'italic', 'strikethrough', 'underline', 'code', 'subscript', 'superscript', 'link',
'|',
'highlight', 'fontSize', 'fontFamily', 'fontColor', 'fontBackgroundColor',
'|',
'bulletedList', 'numberedList',
'|',
'blockQuote', 'insertImage', 'insertTable', 'codeBlock',
'|',
'htmlEmbed',
'|',
'alignment', 'outdent', 'indent',
'|',
'pageBreak', 'horizontalLine',
'|',
'textPartLanguage',
'|',
'sourceEditing',
'|',
'undo', 'redo', 'findAndReplace'
],
cloudServices: CS_CONFIG,
placeholder: 'Type the content here!',
list: {
properties: {
styles: true,
startIndex: true,
reversed: true
}
}
} )
.then( editor => {
window.editor = editor;
} )
.catch( err => {
console.error( err.stack );
} );
Empty file.
Loading