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

Commit

Permalink
Added support for percents.
Browse files Browse the repository at this point in the history
  • Loading branch information
Reinmar committed Aug 16, 2019
1 parent 197b0da commit 51bbc5f
Show file tree
Hide file tree
Showing 2 changed files with 98 additions and 94 deletions.
92 changes: 48 additions & 44 deletions src/widgetresize/resizer.js
Expand Up @@ -95,37 +95,44 @@ export default class Resizer {
begin( domResizeHandle ) {
this.state = new ResizeState( this._options );

this.sizeUI.bindToState( this.state );
this.sizeUI.bindToState( this._options, this.state );

this.state.begin( domResizeHandle, this._getHandleHost() );
this.state.begin( domResizeHandle, this._getHandleHost(), this._getResizeHost() );

this.redraw();
}

_newGetResizeHost() {
return this._getResizeHost().parentElement;
}

updateSize( domEventData ) {
const resizeHost = this._getResizeHost();
const newResizeHost = this._newGetResizeHost();
const domHandleHost = this._getHandleHost();
const domResizeHost = this._getResizeHost();
const unit = this._options.unit;
const newSize = this._proposeNewSize( domEventData );

resizeHost.style.width = null;
newResizeHost.style.width = newSize.width + 'px';
domResizeHost.style.width = ( unit === '%' ? newSize.widthPercents : newSize.width ) + this._options.unit;

const domHandleHostRect = new Rect( domHandleHost );

newSize.handleHostWidth = Math.round( domHandleHostRect.width );
newSize.handleHostHeight = Math.round( domHandleHostRect.height );

const domResizeHostRect = new Rect( domHandleHost );

newSize.width = Math.round( domResizeHostRect.width );
newSize.height = Math.round( domResizeHostRect.height );

this.state.fetchSizeFromElement( resizeHost, newResizeHost );
this.state.update( newSize );

// Refresh values based on the real image. Real image might be limited by max-width, and thus fetching it
// here will reflect this limitation.
this._domResizerWrapper.style.width = this.state.proposedWidth + 'px';
this._domResizerWrapper.style.height = this.state.proposedHeight + 'px';
this._domResizerWrapper.style.width = newSize.handleHostWidth + 'px';
this._domResizerWrapper.style.height = newSize.handleHostHeight + 'px';
}

commit() {
this.state.fetchSizeFromElement( this._getResizeHost(), this._newGetResizeHost() );
const unit = this._options.unit;
const newValue = ( unit === '%' ? this.state.proposedWidthPercents : this.state.proposedWidth ) + this._options.unit;

this._options.onCommit( this.state );
this._options.onCommit( newValue );

this._cleanup();
}
Expand All @@ -141,14 +148,15 @@ export default class Resizer {
this.cancel();
}

// TODO review this
redraw() {
const domWrapper = this._domResizerWrapper;

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

domWrapper.style.width = clientRect.width + 'px';
domWrapper.style.height = clientRect.height + 'px';
Expand All @@ -157,12 +165,12 @@ export default class Resizer {
// 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( resizingHost ) ) {
domWrapper.style.left = resizingHost.offsetLeft + 'px';
domWrapper.style.top = resizingHost.offsetTop + 'px';
if ( !widgetWrapper.isSameNode( handleHost ) ) {
domWrapper.style.left = handleHost.offsetLeft + 'px';
domWrapper.style.top = handleHost.offsetTop + 'px';

domWrapper.style.height = resizingHost.offsetHeight + 'px';
domWrapper.style.width = resizingHost.offsetWidth + 'px';
domWrapper.style.height = handleHost.offsetHeight + 'px';
domWrapper.style.width = handleHost.offsetWidth + 'px';
}
}

Expand Down Expand Up @@ -195,14 +203,13 @@ export default class Resizer {
* @private
* @param {Event} domEventData Event data that caused the size update request. It should be used to calculate the proposed size.
* @returns {Object} return
* @returns {Number} return.x Proposed width.
* @returns {Number} return.y Proposed height.
* @returns {Number} return.width Proposed width.
* @returns {Number} return.height Proposed height.
*/
_proposeNewSize( domEventData ) {
const state = this.state;
const currentCoordinates = extractCoordinates( domEventData );
const isCentered = this._options.isCentered ? this._options.isCentered( this ) : true;
const originalSize = state.originalSize;

// Enlargement defines how much the resize host has changed in a given axis. Naturally it could be a negative number
// meaning that it has been shrunk.
Expand All @@ -217,12 +224,12 @@ export default class Resizer {
// <-->
// enlarge x
const enlargement = {
x: state._referenceCoordinates.x - ( currentCoordinates.x + originalSize.width ),
y: ( currentCoordinates.y - originalSize.height ) - state._referenceCoordinates.y
x: state._referenceCoordinates.x - ( currentCoordinates.x + state.originalWidth ),
y: ( currentCoordinates.y - state.originalHeight ) - state._referenceCoordinates.y
};

if ( isCentered && state.activeHandlePosition.endsWith( '-right' ) ) {
enlargement.x = currentCoordinates.x - ( state._referenceCoordinates.x + originalSize.width );
enlargement.x = currentCoordinates.x - ( state._referenceCoordinates.x + state.originalWidth );
}

// Objects needs to be resized twice as much in horizontal axis if centered, since enlargement is counted from
Expand All @@ -235,8 +242,8 @@ export default class Resizer {

// The size proposed by the user. It does not consider the aspect ratio.
const proposedSize = {
width: Math.abs( originalSize.width + enlargement.x ),
height: Math.abs( originalSize.height + enlargement.y )
width: Math.abs( state.originalWidth + enlargement.x ),
height: Math.abs( state.originalHeight + enlargement.y )
};

// Dominant determination must take the ratio into account.
Expand All @@ -256,10 +263,9 @@ export default class Resizer {
}

return {
// TODO I couldn't find an automated TC for that, but rounding must be done here.
// Do not move it above.
width: Math.round( targetSize.width ),
height: Math.round( targetSize.height )
height: Math.round( targetSize.height ),
widthPercents: Math.min( Math.round( state.originalWidthPercents / state.originalWidth * targetSize.width * 100 ) / 100, 100 )
};
}

Expand All @@ -274,12 +280,11 @@ export default class Resizer {
_getResizeHost() {
const widgetWrapper = this._domResizerWrapper.parentElement;

return this._options.getResizeHost ?
this._options.getResizeHost( widgetWrapper ) : widgetWrapper;
return this._options.getResizeHost( widgetWrapper );
}

/**
* Method used to obtain the resize host.
* Method used to obtain the handle host.
*
* Handle host is an object to which the handles are aligned to.
*
Expand All @@ -292,8 +297,7 @@ export default class Resizer {
_getHandleHost() {
const widgetWrapper = this._domResizerWrapper.parentElement;

return this._options.getHandleHost ?
this._options.getHandleHost( widgetWrapper ) : widgetWrapper;
return this._options.getHandleHost( widgetWrapper );
}

/**
Expand Down Expand Up @@ -374,19 +378,19 @@ class SizeView extends View {
} );
}

bindToState( resizerState ) {
bindToState( options, resizerState ) {
this.bind( 'isVisible' ).to( resizerState, 'proposedWidth', resizerState, 'proposedHeight', ( width, height ) =>
width !== null && height !== null );

this.bind( 'label' ).to(
resizerState, 'proposedWidth',
resizerState, 'proposedHeight',
resizerState, 'proposedHandleHostWidth',
resizerState, 'proposedHandleHostHeight',
resizerState, 'proposedWidthPercents',
( width, height, widthPercents ) => {
if ( widthPercents ) {
return `${ widthPercents }%`;
} else {
if ( options.unit === 'px' ) {
return `${ width }×${ height }`;
} else {
return `${ widthPercents }%`;
}
}
);
Expand Down
100 changes: 50 additions & 50 deletions src/widgetresize/resizerstate.js
Expand Up @@ -23,14 +23,24 @@ export default class ResizeState {
*/
constructor( options ) {
/**
* The size of resize host before current resize process.
* TODO
*
* This information is only known after DOM was rendered, so it will be updated later.
* @readonly
* @member {Number} #originalWidth
*/

/**
* TODO
*
* It contains an object with `width` and `height` properties.
* @readonly
* @member {Number} #originalHeight
*/

/**
* TODO
*
* @readonly
* @member {Object} #originalSize
* @member {Number} #originalWidthPercents
*/

/**
Expand All @@ -44,35 +54,34 @@ export default class ResizeState {
this.set( 'activeHandlePosition', null );

/**
* Width proposed (but not yet accepted) using the widget resizer.
*
* It goes back to `null` once the resizer is dismissed or accepted.
* TODO
*
* @readonly
* @observable
* @member {Number|null} #proposedWidth
* @member {Number|null} #proposedWidthPercents
*/
this.set( 'proposedWidth', null );
this.set( 'proposedWidthPercents', null );

/**
* Height proposed (but not yet accepted) using the widget resizer.
*
* It goes back to `null` once the resizer is dismissed or accepted.
* TODO
*
* @readonly
* @observable
* @member {Number|null} #proposedHeight
* @member {Number|null} #proposedWidthPixels
*/
this.set( 'proposedHeight', null );
this.set( 'proposedWidth', null );

/**
* TODO
*
* @readonly
* @observable
* @member {Number|null} #proposedWidthPercents
* @member {Number|null} #proposedHeightPixels
*/
this.set( 'proposedWidthPercents', null );
this.set( 'proposedHeight', null );

this.set( 'proposedHandleHostWidth', null );
this.set( 'proposedHandleHostHeight', null );

/**
* TODO
Expand Down Expand Up @@ -101,51 +110,42 @@ export default class ResizeState {
/**
*
* @param {HTMLElement} domResizeHandle The handle used to calculate the reference point.
* @param {HTMLElement} domHandleHost
* @param {HTMLElement} domResizeHost
*/
begin( domResizeHandle, handleHost ) {
const clientRect = new Rect( handleHost );
begin( domResizeHandle, domHandleHost, domResizeHost ) {
const clientRect = new Rect( domHandleHost );

this.activeHandlePosition = getHandlePosition( domResizeHandle );

this._referenceCoordinates = getAbsoluteBoundaryPoint( handleHost, getOppositePosition( this.activeHandlePosition ) );
this._referenceCoordinates = getAbsoluteBoundaryPoint( domHandleHost, getOppositePosition( this.activeHandlePosition ) );

this.originalSize = {
width: clientRect.width,
height: clientRect.height
};
this.originalWidth = clientRect.width;
this.originalHeight = clientRect.height;

this.aspectRatio = this._options.getAspectRatio ?
this._options.getAspectRatio( handleHost ) : clientRect.width / clientRect.height;
}
this.aspectRatio = clientRect.width / clientRect.height;

/**
* Sets `proposedWidth` / `proposedHeight` properties based on provided element.
*
* @param {HTMLElement} domHandleHost
* @param {HTMLElement} domResizeHost
*/
fetchSizeFromElement( domHandleHost, domResizeHost ) {
// TODO the logic from this method should be moved to resizer.js.
// Search for other places where we do rounding – it's all in resizer.js.
// TODO this needs an automated test – currently it only affects the SizeView
// which we don't test.
const rect = new Rect( domHandleHost );

this.proposedWidth = Math.round( rect.width );
this.proposedHeight = Math.round( rect.height );

if ( [ 'percent', '%' ].includes( this._options.unit ) ) {
this._proposePercents( new Rect( domResizeHost ), domResizeHost );
const widthStyle = domResizeHost.style.width;

if ( widthStyle ) {
if ( widthStyle.match( /^\d+\.?\d*%$/ ) ) {
this.originalWidthPercents = parseFloat( widthStyle );
} else {
// TODO we need to check it via comparing to the parent's width.
this.originalWidthPercents = 50;
}
} else {
this.originalWidthPercents = 100;
}
}

_proposePercents( rect, domResizeHost ) {
const hostParentElement = domResizeHost.parentElement;
// We need to use getComputedStyle instead of bounding rect, as rect width also include parent's padding, that **can not**
// be occupied by the resized object. Computed width does not include it.
const hostParentInnerWith = parseFloat( hostParentElement.ownerDocument.defaultView.getComputedStyle( hostParentElement ).width );
update( newSize ) {
this.proposedWidth = newSize.width;
this.proposedHeight = newSize.height;
this.proposedWidthPercents = newSize.widthPercents;

this.proposedWidthPercents = Math.min( Math.round( rect.width / hostParentInnerWith * 100 ), 100 );
this.proposedHandleHostWidth = newSize.handleHostWidth;
this.proposedHandleHostHeight = newSize.handleHostHeight;
}
}

Expand Down

0 comments on commit 51bbc5f

Please sign in to comment.