]> source.dussan.org Git - jquery-ui.git/commitdiff
Resizable: Fix content shrink on resize
authorDaniel García <93217193+Daniel-Garmig@users.noreply.github.com>
Mon, 9 Sep 2024 21:51:57 +0000 (23:51 +0200)
committerGitHub <noreply@github.com>
Mon, 9 Sep 2024 21:51:57 +0000 (23:51 +0200)
Make resizable elements not shrink on resize when they have scrollbars
and "box-sizing: content-box".

Fixes: gh-2277
Closes gh-2281

tests/unit/resizable/core.js
tests/unit/resizable/options.js
ui/widgets/resizable.js

index b3c61514a98eb77e12af7b9662fc64617dfd7ace..b47f0b645f46378bcc2fa1d5ab6f3725e2369a61 100644 (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)" );
+}
+
 } );
index add8d80344888767757e88ead3fd6541c056dddd..b80c051d5883daf0d932cd939a1f70bd2b734999 100644 (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" );
+}
+
 } );
index 1698d55e8bb153e4a02ff8e04ed58cfd4abe793e..6f1e0ebdecb885205cb8853164f4d9486e1a197f 100644 (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" ) )
                        } );
                } );