Skip to content
This repository has been archived by the owner on Jun 26, 2020. It is now read-only.

Commit

Permalink
Merge pull request #112 from ckeditor/i/5189
Browse files Browse the repository at this point in the history
Fix: Fixed image resize behavior upon short clicking a handle without dragging. Image will no longer became full width, nor will it briefly flash an unexpected size. Closes ckeditor/ckeditor5#5189. Closes ckeditor/ckeditor5#5195.

MINOR BREAKING CHANGE: Resizer options object now also takes an editor instance.
  • Loading branch information
Reinmar authored Jan 3, 2020
2 parents b9cf062 + 974c58d commit d6a5c93
Show file tree
Hide file tree
Showing 2 changed files with 101 additions and 51 deletions.
29 changes: 25 additions & 4 deletions src/widgetresize.js
Original file line number Diff line number Diff line change
Expand Up @@ -135,9 +135,28 @@ export default class WidgetResize extends Plugin {
*/
attachTo( options ) {
const resizer = new Resizer( options );
const plugins = this.editor.plugins;

resizer.attach();

if ( plugins.has( 'WidgetToolbarRepository' ) ) {
// Hiding widget toolbar to improve the performance
// (https://github.com/ckeditor/ckeditor5-widget/pull/112#issuecomment-564528765).
const widgetToolbarRepository = plugins.get( 'WidgetToolbarRepository' );

resizer.on( 'begin', () => {
widgetToolbarRepository.forceDisabled( 'resize' );
}, { priority: 'lowest' } );

resizer.on( 'cancel', () => {
widgetToolbarRepository.clearForceDisabled( 'resize' );
}, { priority: 'highest' } );

resizer.on( 'commit', () => {
widgetToolbarRepository.clearForceDisabled( 'resize' );
}, { priority: 'highest' } );
}

this._resizers.set( options.viewElement, resizer );

return resizer;
Expand Down Expand Up @@ -179,15 +198,17 @@ mix( WidgetResize, ObservableMixin );
*/

/**
* @member {module:engine/model/element~Element} module:widget/widgetresize~ResizerOptions#modelElement
* Editor instance associated with the resizer.
*
* @member {module:core/editor/editor~Editor} module:widget/widgetresize~ResizerOptions#editor
*/

/**
* @member {module:engine/view/containerelement~ContainerElement} module:widget/widgetresize~ResizerOptions#viewElement
* @member {module:engine/model/element~Element} module:widget/widgetresize~ResizerOptions#modelElement
*/

/**
* @member {module:engine/view/downcastwriter~DowncastWriter} module:widget/widgetresize~ResizerOptions#downcastWriter
* @member {module:engine/view/containerelement~ContainerElement} module:widget/widgetresize~ResizerOptions#viewElement
*/

/**
Expand All @@ -200,9 +221,9 @@ mix( WidgetResize, ObservableMixin );
*
* ```js
* {
* editor,
* modelElement: data.item,
* viewElement: widget,
* downcastWriter: conversionApi.writer,
*
* onCommit( newValue ) {
* editor.execute( 'imageResize', { width: newValue } );
Expand Down
123 changes: 76 additions & 47 deletions src/widgetresize/resizer.js
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,14 @@ export default class Resizer {
*/
this._domResizerWrapper = null;

/**
* A wrapper that is controlled by the resizer. This is usually a widget element.
*
* @private
* @type {module:engine/view/element~Element|null}
*/
this._viewResizerWrapper = null;

/**
* @observable
*/
Expand All @@ -71,6 +79,15 @@ export default class Resizer {
this.decorate( 'cancel' );
this.decorate( 'commit' );
this.decorate( 'updateSize' );

this.on( 'commit', event => {
// State might not be initialized yet. In this case, prevent further handling and make sure that the resizer is
// cleaned up (#5195).
if ( !this.state.proposedWidth ) {
this._cleanup();
event.stop();
}
}, { priority: 'high' } );
}

/**
Expand All @@ -79,30 +96,34 @@ export default class Resizer {
attach() {
const that = this;
const widgetElement = this._options.viewElement;
const writer = this._options.downcastWriter;
const editingView = this._options.editor.editing.view;

editingView.change( writer => {
const viewResizerWrapper = writer.createUIElement( 'div', {
class: 'ck ck-reset_all ck-widget__resizer'
}, function( domDocument ) {
const domElement = this.toDomElement( domDocument );

that._appendHandles( domElement );
that._appendSizeUI( domElement );

const viewResizerWrapper = writer.createUIElement( 'div', {
class: 'ck ck-reset_all ck-widget__resizer'
}, function( domDocument ) {
const domElement = this.toDomElement( domDocument );
that._domResizerWrapper = domElement;

that._appendHandles( domElement );
that._appendSizeUI( domElement );
that.on( 'change:isEnabled', ( evt, propName, newValue ) => {
domElement.style.display = newValue ? '' : 'none';
} );

that._domResizerWrapper = domElement;
domElement.style.display = that.isEnabled ? '' : 'none';

that.on( 'change:isEnabled', ( evt, propName, newValue ) => {
domElement.style.display = newValue ? '' : 'none';
return domElement;
} );

domElement.style.display = that.isEnabled ? '' : 'none';
// Append the resizer wrapper to the widget's wrapper.
writer.insert( writer.createPositionAt( widgetElement, 'end' ), viewResizerWrapper );
writer.addClass( 'ck-widget_with-resizer', widgetElement );

return domElement;
this._viewResizerWrapper = viewResizerWrapper;
} );

// Append the resizer wrapper to the widget's wrapper.
writer.insert( writer.createPositionAt( widgetElement, 'end' ), viewResizerWrapper );
writer.addClass( 'ck-widget_with-resizer', widgetElement );
}

/**
Expand All @@ -128,13 +149,20 @@ export default class Resizer {
* @param {Event} domEventData
*/
updateSize( domEventData ) {
const domHandleHost = this._getHandleHost();
const domResizeHost = this._getResizeHost();
const unit = this._options.unit;
const newSize = this._proposeNewSize( domEventData );
const editingView = this._options.editor.editing.view;

domResizeHost.style.width = ( unit === '%' ? newSize.widthPercents : newSize.width ) + this._options.unit;
editingView.change( writer => {
const unit = this._options.unit;
const newWidth = ( unit === '%' ? newSize.widthPercents : newSize.width ) + unit;

writer.setStyle( 'width', newWidth, this._options.viewElement );
} );

// Get an actual image width, and:
// * reflect this size to the resize wrapper
// * apply this **real** size to the state
const domHandleHost = this._getHandleHost();
const domHandleHostRect = new Rect( domHandleHost );

newSize.handleHostWidth = Math.round( domHandleHostRect.width );
Expand Down Expand Up @@ -187,36 +215,37 @@ export default class Resizer {
* @param {module:utils/dom/rect~Rect} [handleHostRect] Handle host rectangle might be given to improve performance.
*/
redraw( handleHostRect ) {
// TODO review this
const domWrapper = this._domResizerWrapper;

if ( existsInDom( domWrapper ) ) {
// Refresh only if resizer exists in the DOM.
const widgetWrapper = domWrapper.parentElement;
const handleHost = this._getHandleHost();
const clientRect = handleHostRect || new Rect( handleHost );

domWrapper.style.width = clientRect.width + 'px';
domWrapper.style.height = clientRect.height + 'px';

const offsets = {
left: handleHost.offsetLeft,
top: handleHost.offsetTop,
height: handleHost.offsetHeight,
width: handleHost.offsetWidth
};

// In case a resizing host is not a widget wrapper, we need to compensate
// for any additional offsets the resize host might have. E.g. wrapper padding
// or simply another editable. By doing that the border and resizers are shown
// only around the resize host.
if ( !widgetWrapper.isSameNode( handleHost ) ) {
domWrapper.style.left = offsets.left + 'px';
domWrapper.style.top = offsets.top + 'px';

domWrapper.style.height = offsets.height + 'px';
domWrapper.style.width = offsets.width + 'px';
}
this._options.editor.editing.view.change( writer => {
// Refresh only if resizer exists in the DOM.
const widgetWrapper = domWrapper.parentElement;
const handleHost = this._getHandleHost();
const clientRect = handleHostRect || new Rect( handleHost );

writer.setStyle( 'width', clientRect.width + 'px', this._viewResizerWrapper );
writer.setStyle( 'height', clientRect.height + 'px', this._viewResizerWrapper );

const offsets = {
left: handleHost.offsetLeft,
top: handleHost.offsetTop,
height: handleHost.offsetHeight,
width: handleHost.offsetWidth
};

// In case a resizing host is not a widget wrapper, we need to compensate
// for any additional offsets the resize host might have. E.g. wrapper padding
// or simply another editable. By doing that the border and resizers are shown
// only around the resize host.
if ( !widgetWrapper.isSameNode( handleHost ) ) {
writer.setStyle( 'left', offsets.left + 'px', this._viewResizerWrapper );
writer.setStyle( 'top', offsets.top + 'px', this._viewResizerWrapper );

writer.setStyle( 'height', offsets.height + 'px', this._viewResizerWrapper );
writer.setStyle( 'width', offsets.width + 'px', this._viewResizerWrapper );
}
} );
}

function existsInDom( element ) {
Expand Down

0 comments on commit d6a5c93

Please sign in to comment.