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

Improved images preview while dragging them. #14977

Closed
Closed
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
37 changes: 35 additions & 2 deletions packages/ckeditor5-clipboard/src/dragdropexperimental.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ import {
createElement,
DomEmitterMixin,
delay,
Rect,
type DelayedFunc,
type ObservableChangeEvent,
type DomEmitter
Expand All @@ -56,6 +57,8 @@ import DragDropTarget from './dragdroptarget';

import '../theme/clipboard.css';

const CONDITIONAL_WIDTH_ELEMENTS = [ 'imageInline', 'imageBlock' ];

// Drag and drop events overview:
//
// ┌──────────────────┐
Expand Down Expand Up @@ -639,16 +642,46 @@ export default class DragDropExperimental extends Plugin {
const preview = createElement( global.document, 'div' );

preview.className = 'ck ck-content';
preview.style.width = computedStyle.width;

preview.innerHTML = dataTransfer.getData( 'text/html' );

if ( this._draggableElement && CONDITIONAL_WIDTH_ELEMENTS.includes( this._draggableElement.name ) ) {
const resizedImageInPreview: HTMLElement | null = preview.querySelector( '.image_resized' );

if ( resizedImageInPreview ) {
resizedImageInPreview.style.width = '100%';
}

const draggableElementRect = new Rect( view.domConverter.viewRangeToDom( view.document.selection.getFirstRange()! ) );
const editableRect = new Rect( this.editor.ui.view.editable.element! );

preview.style.maxWidth = calculateImagePreviewWidth( draggableElementRect, editableRect ) + 'px';
} else {
preview.style.width = computedStyle.width;
}

dataTransfer.setDragImage( preview, 0, 0 );
// TODO set x to make dragged widget stick to the mouse cursor

this._previewContainer.appendChild( preview );
}
}

/**
* Calculates the image preview width during dragging (image preview cannot be bigger than 50% of editor area
* while user drags the image by clicking on it).
*/

function calculateImagePreviewWidth( draggableElementRect: Rect, editableRect: Rect ): number {
const editableWidth = editableRect.width;
const draggableElementWidth = draggableElementRect.width;

if ( draggableElementWidth <= editableWidth / 2 ) {
return draggableElementWidth;
}

return editableWidth / 2;
}

/**
* Returns the drop effect that should be a result of dragging the content.
* This function is handling a quirk when checking the effect in the 'drop' DOM event.
Expand Down
113 changes: 112 additions & 1 deletion packages/ckeditor5-clipboard/tests/dragdropexperimental.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ import HorizontalLine from '@ckeditor/ckeditor5-horizontal-line/src/horizontalli
import ShiftEnter from '@ckeditor/ckeditor5-enter/src/shiftenter';
import BlockQuote from '@ckeditor/ckeditor5-block-quote/src/blockquote';
import Bold from '@ckeditor/ckeditor5-basic-styles/src/bold';
import Image from '@ckeditor/ckeditor5-image/src/image';
import ImageResize from '@ckeditor/ckeditor5-image/src/imageresize';
import env from '@ckeditor/ckeditor5-utils/src/env';

import testUtils from '@ckeditor/ckeditor5-core/tests/_utils/utils';
Expand Down Expand Up @@ -64,7 +66,8 @@ describe( 'Drag and Drop experimental', () => {
document.body.appendChild( editorElement );

editor = await ClassicTestEditor.create( editorElement, {
plugins: [ DragDropExperimental, PastePlainText, Paragraph, Table, HorizontalLine, ShiftEnter, BlockQuote, Bold ]
plugins: [ DragDropExperimental, PastePlainText, Paragraph, Table, Image, ImageResize,
HorizontalLine, ShiftEnter, BlockQuote, Bold ]
} );

model = editor.model;
Expand Down Expand Up @@ -997,6 +1000,114 @@ describe( 'Drag and Drop experimental', () => {
);
} );

it( 'should start dragging by grabbing the image element and show shrink preview', () => {
testUtils.sinon.stub( editor.editing.view.getDomRoot(), 'getBoundingClientRect' ).returns( {
top: 0,
left: 0,
right: 500,
width: 500,
bottom: 100,
height: 100
} );

setModelData( model,
'<paragraph>foo</paragraph>' +
'[<imageBlock src="/assets/sample.png" width="100%"></imageBlock>]' +
'<paragraph>baz</paragraph>'
);

const dataTransferMock = createDataTransfer();
const spyClipboardOutput = sinon.spy();
const spyClipboardInput = sinon.spy();

viewDocument.on( 'clipboardOutput', ( event, data ) => spyClipboardOutput( data ) );
viewDocument.on( 'clipboardInput', ( event, data ) => spyClipboardInput( data ) );

const widgetViewElement = viewDocument.getRoot().getChild( 1 );
const domNode = domConverter.mapViewToDom( widgetViewElement );

const eventData = {
domTarget: domNode,
target: widgetViewElement,
domEvent: {}
};

viewDocument.fire( 'mousedown', {
...eventData
} );

viewDocument.fire( 'dragstart', {
...eventData,
dataTransfer: dataTransferMock,
stopPropagation: () => {}
} );

expect( dataTransferMock.getData( 'text/html' ) ).to.equal(
'<figure class="image image_resized" style="width:100%;">' +
'<img src="/assets/sample.png"></figure>' );

expect( widgetViewElement.getAttribute( 'draggable' ) ).to.equal( 'true' );

expect( spyClipboardOutput.called ).to.be.true;

fireDragEnd( dataTransferMock );
expectFinalized();
} );

it( 'should start dragging by grabbing the image element and show original size preview', () => {
testUtils.sinon.stub( editor.editing.view.getDomRoot(), 'getBoundingClientRect' ).returns( {
top: 0,
left: 0,
right: 500,
width: 500,
bottom: 100,
height: 100
} );

setModelData( model,
'<paragraph>foo</paragraph>' +
'[<imageBlock src="/assets/sample.png" width="100px"></imageBlock>]' +
'<paragraph>baz</paragraph>'
);

const dataTransferMock = createDataTransfer();
const spyClipboardOutput = sinon.spy();
const spyClipboardInput = sinon.spy();

viewDocument.on( 'clipboardOutput', ( event, data ) => spyClipboardOutput( data ) );
viewDocument.on( 'clipboardInput', ( event, data ) => spyClipboardInput( data ) );

const widgetViewElement = viewDocument.getRoot().getChild( 1 );
const domNode = domConverter.mapViewToDom( widgetViewElement );

const eventData = {
domTarget: domNode,
target: widgetViewElement,
domEvent: {}
};

viewDocument.fire( 'mousedown', {
...eventData
} );

viewDocument.fire( 'dragstart', {
...eventData,
dataTransfer: dataTransferMock,
stopPropagation: () => {}
} );

expect( dataTransferMock.getData( 'text/html' ) ).to.equal(
'<figure class="image image_resized" style="width:100px;">' +
'<img src="/assets/sample.png"></figure>' );

expect( widgetViewElement.getAttribute( 'draggable' ) ).to.equal( 'true' );

expect( spyClipboardOutput.called ).to.be.true;

fireDragEnd( dataTransferMock );
expectFinalized();
} );

it( 'should start dragging the selected text fragment', () => {
setModelData( model,
'<paragraph>[foo]bar</paragraph>'
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.