Resizable: Fix content shrink on resize

Make resizable elements not shrink on resize when they have scrollbars
and "box-sizing: content-box".

Fixes: gh-2277
Closes gh-2281
This commit is contained in:
Daniel García 2024-09-09 23:51:57 +02:00 committed by GitHub
parent d564731f20
commit c934995efa
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 191 additions and 17 deletions

View File

@ -244,4 +244,72 @@ QUnit.test( "nested resizable", function( assert ) {
outer.remove();
} );
QUnit.test( "Resizable with scrollbars and box-sizing: border-box", function( assert ) {
assert.expect( 4 );
testResizableWithBoxSizing( assert, {
isBorderBox: true,
applyScaleTransform: false
} );
} );
QUnit.test( "Resizable with scrollbars and box-sizing: content-box", function( assert ) {
assert.expect( 4 );
testResizableWithBoxSizing( assert, {
isBorderBox: false,
applyScaleTransform: false
} );
} );
QUnit.test( "Resizable with scrollbars, a transform and box-sizing: border-box", function( assert ) {
assert.expect( 4 );
testResizableWithBoxSizing( assert, {
isBorderBox: true,
applyScaleTransform: true
} );
} );
QUnit.test( "Resizable with scrollbars, a transform and box-sizing: content-box", function( assert ) {
assert.expect( 4 );
testResizableWithBoxSizing( assert, {
isBorderBox: false,
applyScaleTransform: true
} );
} );
function testResizableWithBoxSizing( assert, options ) {
var widthBefore, heightBefore,
cssBoxSizing = options.isBorderBox ? "border-box" : "content-box",
cssTransform = options.applyScaleTransform ? "scale(1.5)" : "",
elementContent = $( "<div>" )
.css( {
width: "200px",
height: "200px",
padding: "10px",
border: "5px",
borderStyle: "solid",
margin: "20px"
} )
.appendTo( "#resizable1" ),
element = $( "#resizable1" ).css( { overflow: "auto", transform: cssTransform } ).resizable(),
handle = ".ui-resizable-se";
$( "<style> * { box-sizing: " + cssBoxSizing + "; } </style>" ).appendTo( "#qunit-fixture" );
// In some browsers scrollbar may change element size (when "box-sizing: content-box")
widthBefore = element.innerWidth();
heightBefore = element.innerHeight();
// Both scrollbars
testHelper.drag( handle, 10, 10 );
assert.equal( parseFloat( element.innerWidth() ), widthBefore + 10, "element width (both scrollbars)" );
assert.equal( parseFloat( element.innerHeight() ), heightBefore + 10, "element height (both scrollbars)" );
// Single (vertical) scrollbar.
elementContent.css( "width", "50px" );
testHelper.drag( handle, 10, 10 );
assert.equal( parseFloat( element.innerWidth() ), widthBefore + 20, "element width (only vertical scrollbar)" );
assert.equal( parseFloat( element.innerHeight() ), heightBefore + 20, "element height (only vertical scrollbar)" );
}
} );

View File

@ -542,21 +542,22 @@ QUnit.test( "alsoResize + multiple selection", function( assert ) {
QUnit.test( "alsoResize with box-sizing: border-box", function( assert ) {
assert.expect( 4 );
$( "<style> * { box-sizing: border-box; } </style>" ).appendTo( "#qunit-fixture" );
var other = $( "<div>" )
.css( {
width: 50,
height: 50,
padding: 10,
border: 5
width: "50px",
height: "50px",
padding: "10px",
border: "5px",
borderStyle: "solid"
} )
.appendTo( "body" ),
.appendTo( "#qunit-fixture" ),
element = $( "#resizable1" ).resizable( {
alsoResize: other
} ),
handle = ".ui-resizable-se";
$( "*" ).css( "box-sizing", "border-box" );
testHelper.drag( handle, 80, 80 );
assert.equal( element.width(), 180, "resizable width" );
@ -565,4 +566,51 @@ QUnit.test( "alsoResize with box-sizing: border-box", function( assert ) {
assert.equal( parseFloat( other.css( "height" ) ), 130, "alsoResize height" );
} );
QUnit.test( "alsoResize with scrollbars and box-sizing: border-box", function( assert ) {
assert.expect( 4 );
testAlsoResizeWithBoxSizing( assert, {
isBorderBox: true
} );
} );
QUnit.test( "alsoResize with scrollbars and box-sizing: content-box", function( assert ) {
assert.expect( 4 );
testAlsoResizeWithBoxSizing( assert, {
isBorderBox: false
} );
} );
function testAlsoResizeWithBoxSizing( assert, options ) {
var widthBefore, heightBefore,
cssBoxSizing = options.isBorderBox ? "border-box" : "content-box",
other = $( "<div>" )
.css( {
width: "150px",
height: "150px",
padding: "10px",
border: "5px",
borderStyle: "solid",
margin: "20px",
overflow: "scroll"
} )
.appendTo( "#qunit-fixture" ),
element = $( "#resizable1" ).resizable( {
alsoResize: other
} ),
handle = ".ui-resizable-se";
$( "<style> * { box-sizing: " + cssBoxSizing + "; } </style>" ).appendTo( "#qunit-fixture" );
// In some browsers scrollbar may change element computed size.
widthBefore = other.innerWidth();
heightBefore = other.innerHeight();
testHelper.drag( handle, 80, 80 );
assert.equal( element.width(), 180, "resizable width" );
assert.equal( parseFloat( other.innerWidth() ), widthBefore + 80, "alsoResize width" );
assert.equal( element.height(), 180, "resizable height" );
assert.equal( parseFloat( other.innerHeight() ), heightBefore + 80, "alsoResize height" );
}
} );

View File

@ -80,12 +80,18 @@ $.widget( "ui.resizable", $.ui.mouse, {
_hasScroll: function( el, a ) {
if ( $( el ).css( "overflow" ) === "hidden" ) {
var scroll,
has = false,
overflow = $( el ).css( "overflow" );
if ( overflow === "hidden" ) {
return false;
}
if ( overflow === "scroll" ) {
return true;
}
var scroll = ( a && a === "left" ) ? "scrollLeft" : "scrollTop",
has = false;
scroll = ( a && a === "left" ) ? "scrollLeft" : "scrollTop";
if ( el[ scroll ] > 0 ) {
return true;
@ -362,7 +368,7 @@ $.widget( "ui.resizable", $.ui.mouse, {
_mouseStart: function( event ) {
var curleft, curtop, cursor,
var curleft, curtop, cursor, calculatedSize,
o = this.options,
el = this.element;
@ -381,20 +387,24 @@ $.widget( "ui.resizable", $.ui.mouse, {
this.offset = this.helper.offset();
this.position = { left: curleft, top: curtop };
if ( !this._helper ) {
calculatedSize = this._calculateAdjustedElementDimensions( el );
}
this.size = this._helper ? {
width: this.helper.width(),
height: this.helper.height()
} : {
width: el.width(),
height: el.height()
width: calculatedSize.width,
height: calculatedSize.height
};
this.originalSize = this._helper ? {
width: el.outerWidth(),
height: el.outerHeight()
} : {
width: el.width(),
height: el.height()
width: calculatedSize.width,
height: calculatedSize.height
};
this.sizeDiff = {
@ -690,6 +700,52 @@ $.widget( "ui.resizable", $.ui.mouse, {
};
},
_calculateAdjustedElementDimensions: function( element ) {
var elWidth, elHeight, paddingBorder,
ce = element.get( 0 );
if ( element.css( "box-sizing" ) !== "content-box" ||
( !this._hasScroll( ce ) && !this._hasScroll( ce, "left" ) ) ) {
return {
height: parseFloat( element.css( "height" ) ),
width: parseFloat( element.css( "width" ) )
};
}
// Check if CSS inline styles are set and use those (usually from previous resizes)
elWidth = parseFloat( ce.style.width );
elHeight = parseFloat( ce.style.height );
paddingBorder = this._getPaddingPlusBorderDimensions( element );
elWidth = isNaN( elWidth ) ?
this._getElementTheoreticalSize( element, paddingBorder, "width" ) :
elWidth;
elHeight = isNaN( elHeight ) ?
this._getElementTheoreticalSize( element, paddingBorder, "height" ) :
elHeight;
return {
height: elHeight,
width: elWidth
};
},
_getElementTheoreticalSize: function( element, extraSize, dimension ) {
// offsetWidth/offsetHeight is a rounded sum of content, padding, scroll gutter, and border
var size = Math.max( 0, Math.ceil(
element.get( 0 )[ "offset" + dimension[ 0 ].toUpperCase() + dimension.slice( 1 ) ] -
extraSize[ dimension ] -
0.5
// If offsetWidth/offsetHeight is unknown, then we can't determine theoretical size.
// Use an explicit zero to avoid NaN.
// See https://github.com/jquery/jquery/issues/3964
) ) || 0;
return size;
},
_proportionallyResize: function() {
if ( !this._proportionallyResizeElements.length ) {
@ -1044,9 +1100,11 @@ $.ui.plugin.add( "resizable", "alsoResize", {
o = that.options;
$( o.alsoResize ).each( function() {
var el = $( this );
var el = $( this ),
elSize = that._calculateAdjustedElementDimensions( el );
el.data( "ui-resizable-alsoresize", {
width: parseFloat( el.css( "width" ) ), height: parseFloat( el.css( "height" ) ),
width: elSize.width, height: elSize.height,
left: parseFloat( el.css( "left" ) ), top: parseFloat( el.css( "top" ) )
} );
} );