From 457bc6dd049d0e979771fff18462815820ac6bbe Mon Sep 17 00:00:00 2001 From: pharret31 Date: Wed, 11 Mar 2026 11:06:23 +0100 Subject: [PATCH 1/4] fix --- .../js/__internal/ui/splitter/splitter.ts | 5 +- .../DevExpress.ui.widgets/splitter.tests.js | 73 ++++++++++++++++++- 2 files changed, 74 insertions(+), 4 deletions(-) diff --git a/packages/devextreme/js/__internal/ui/splitter/splitter.ts b/packages/devextreme/js/__internal/ui/splitter/splitter.ts index e41a0c5e7821..bcd463fb1c9d 100644 --- a/packages/devextreme/js/__internal/ui/splitter/splitter.ts +++ b/packages/devextreme/js/__internal/ui/splitter/splitter.ts @@ -1105,9 +1105,10 @@ class Splitter extends CollectionWidgetLiveUpdate { } _dimensionChanged(): void { - this._updateItemSizes(); - this._layout = this._getDefaultLayoutBasedOnSize(); + this._applyStylesFromLayout(this._layout); + + this._updateItemSizes(); } _optionChanged(args: OptionChanged): void { diff --git a/packages/devextreme/testing/tests/DevExpress.ui.widgets/splitter.tests.js b/packages/devextreme/testing/tests/DevExpress.ui.widgets/splitter.tests.js index 03b6d72df350..cc66c28968a3 100644 --- a/packages/devextreme/testing/tests/DevExpress.ui.widgets/splitter.tests.js +++ b/packages/devextreme/testing/tests/DevExpress.ui.widgets/splitter.tests.js @@ -2122,8 +2122,8 @@ QUnit.module('Resizing', moduleConfig, () => { this.instance.option(orientation === 'horizontal' ? 'width' : 'height', 700); - this.checkItemSizes([159.133, 159.133, 159.133, 204.602]); - this.assertLayout([23.3333, 23.3333, 23.3333, 30]); + this.checkItemSizes([0, 166.664, 215.336, 300]); + this.assertLayout([0, 24.4375, 31.5742, 43.9883]); }); }); @@ -2971,6 +2971,75 @@ QUnit.module('Resizing', moduleConfig, () => { this.assertLayout(['15', '10', '25', '50']); }); + + [{ + initialWidth: 408, + newWidth: 808, + dataSource: [{ size: '200px', maxSize: '200px' }, { }], + expectedLayout: ['25', '75'], + expectedItemSizes: [200, 600], + }, { + initialWidth: 408, + newWidth: 808, + dataSource: [{ }, { size: '200px', maxSize: '200px' }], + expectedLayout: ['75', '25'], + expectedItemSizes: [600, 200], + }, { + initialWidth: 808, + newWidth: 408, + dataSource: [{ size: '200px', minSize: '200px' }, { }], + expectedLayout: ['50', '50'], + expectedItemSizes: [200, 200], + }, { + initialWidth: 808, + newWidth: 408, + dataSource: [{ }, { size: '200px', minSize: '200px' }], + expectedLayout: ['50', '50'], + expectedItemSizes: [200, 200], + }, { + initialWidth: 416, + newWidth: 816, + dataSource: [{ size: '200px', maxSize: '200px' }, { }, { }], + expectedLayout: ['25', '12.5', '62.5'], + expectedItemSizes: [200, 100, 500], + }, { + initialWidth: 816, + newWidth: 416, + dataSource: [{ size: '200px', minSize: '200px' }, { }, { }], + expectedLayout: ['50', '50', '0'], + expectedItemSizes: [200, 200, 0], + }, { + initialWidth: 416, + newWidth: 816, + dataSource: [{ }, { size: '200px', maxSize: '200px' }, { }], + expectedLayout: ['12.5', '25', '62.5'], + expectedItemSizes: [100, 200, 500], + }, { + initialWidth: 416, + newWidth: 816, + dataSource: [{ size: '200px', minSize: '100px', maxSize: '300px' }, { }], + expectedLayout: ['24.7525', '75.2475'], + expectedItemSizes: [200, 608], + }, { + initialWidth: 208, + newWidth: 808, + dataSource: [{ size: '100px', maxSize: '200px' }, { }], + expectedLayout: ['12.5', '87.5'], + expectedItemSizes: [100, 700], + }].forEach(({ initialWidth, newWidth, dataSource, expectedLayout, expectedItemSizes }) => { + QUnit.test(`pane constraints should be respected after dimension change from ${initialWidth} to ${newWidth}, dataSource: ${JSON.stringify(dataSource)}`, function(assert) { + this.reinit({ + width: initialWidth, + height: 408, + dataSource, + }); + + this.instance.option('width', newWidth); + + this.checkItemSizes(expectedItemSizes); + this.assertLayout(expectedLayout); + }); + }); }); QUnit.module('Initialization', moduleConfig, () => { From 294a8492205c3e32f14fb691a44f1d981d284552 Mon Sep 17 00:00:00 2001 From: pharret31 Date: Wed, 11 Mar 2026 12:15:11 +0100 Subject: [PATCH 2/4] rebase --- ...ane_1.size=`100px` (fluent.blue.light).png | Bin 6339 -> 6323 bytes .../tests/navigation/splitter/resize.ts | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/e2e/testcafe-devextreme/tests/navigation/splitter/etalons/Splitter in tab content after window resize, pane_1.size=`100px` (fluent.blue.light).png b/e2e/testcafe-devextreme/tests/navigation/splitter/etalons/Splitter in tab content after window resize, pane_1.size=`100px` (fluent.blue.light).png index 70f7bfc1600719f7e2397057d169887d2c69b40e..c9bd3743a9238b9f65b8fa8aef4bf22c3f49f707 100644 GIT binary patch literal 6323 zcmeHMXH-+$wni)nf}AU&A_#H_AiX!K2gM|WCcTOZ1du94N(cxFK15mwy#zFX6zMfU z08u(pA~g^M2}N4yEx_A&?ilagJI=Xfj5o&nbAM&*?78Nid(FA#_kDAR>*;`*7_KwW z(9keJA$Rp@XlNlcG^d2mp9B7pkJAbSerTb0e>3zwwRAmjfQIIp^U2k6^Hb*`yO3{d z#mtqe-}`p>){x~00^iMvh^m$+a&u$KH%Mva(kP*3XsmO}BPr#2Q*OD+*-rG!2Xc9v zk`S>7-&5Q?CN|nW#hUQ*Ae7|yGtYX^Q_-ZQ$kik*A=^oRDL*I4jUfrD9z}0Fe(o9e zmOev(DkfD${uTSDWGQ>!)rK8;#J2Eo+%L3plL1l}t_Z84KvwuBrs?ivDXe4XXt{kY zDNPxXAg!(7Im?IwEv%n){RLRXIb63xLNG&7UVF51|5a|^>(QVHIQnS-f}$?dLr@@r zOEDm*KIkSCg@piap|Q__JHMWSqCgt7z~$>Re`hF8r%5-ov3i7*18Z6i56FRiXBL^) zzV>!PP*^9Evx)O$Z)-b3KY*DUpQA1(T)a%{Pq)O^% z!f{is8I{ZWTaY(N<72$uti7slO70Hl^lmtsxKe{LidO!zUp%Q30)nOpv0A>R&R%6p z(FsOR2cPn1Htz2q*102k>Qerqq@@6P?KfQAD*<~c!>4u=`21H_k9@-}Cn-pLtjdk@ zZ2PVpun{GwVz?Er2&uWGPvTg}*;uHqJzi^^J=if?Yi*a!_^}+ev@>5X^a6MmJMb(^ zm(#W8U!Wa2aA?%*d#7$f(sv&6+9O;wlZR~pYQdJi%N*Y`8!42hy=$6W+Pt;N%mw$z zd5xQm+}x@hhg|1xYLaL6mM28CKmACD*VI1Z{(oY&7;*Di7%Z5AVgo@3#?J@*rPIcFk#sMsC?boiu~o<-F^+R78$t2b z{{qX6a`!C@?%^q3`=O^eS}L-+!kOE>k0h6%khqQ(oQv%a$sTBT;NQU{Fo|G-tIn^vP!WYCTWCi3?x_kI`K^j zRU2_bQL{SVB4QkEzxM@Z5J&F1h=N@V*q8Sh4Zfe%3@9v1ATPVjIC*}Rt{Na&-dUT! z^%?C1Nucw`w_UJk>^Q8p?;zENxE0mD>SR&<{)v&RU^+@>(QGSs(AMqv_Ttb4n9N@6 zLH&U41*K09=3>Z^ibJIViN(;158T8#r8)+#+$`O^3QqO08BRba$i?mg$f(#r>&np)OfS~Ncc+C#gd z`U4ivcbfcbsCqbB7fTl4g9*%KcVD)LNWt3SK+FU;3fy0jxd@STu-c8%oBe>nH*0nB z>-4%$eRi3Xac!pkZusDKB553mKwRzw`Cra#{A;VShRF6bV|ham7M_osG&YXh=jI>niDYpjO+!0Q!Y zL&VgIN+YXq-i%6{Zfu#Ty2i?g56DPHFV{!QIL2ceCJ-#e(L(ax&g)Y-d2fNNuU5&$ zJd)LT@L=a=YE;H7#@YYdWJ$PY0&?dUgvqeD35v+|a3Y_pZ}fm^WjU0%f=F9dRvxd+ zP8EsvVmTrz>v_`&apq6c=~C&cx|>{rsN%}X1vSc3(*xB&8Go-oY<+q}z%;gpx>{7m zaVh($0)^u`zS*oWPD4WOSh=35Cix4UbjhF1FYntE%?QR~L=4F$cTd{K=kWPe;v%#r zA)EU6zc5^=MpAjkYHdbE3!$qNNA>d{ z=*N(=7l4XMES{d}h?{3&-!O9SHj$V_Yx16SnfIm@4u45e)yYum_$+K*aUK8|yR(0b z6WEY-d5fXKsXeHu9<)kSx9go%MR6f-i&D%LwuvF=&W6E^&x$?5Ac|ezFHOX0h(ZNj zeGjro%q91Wj+qRhrk)5%9j(h&Ei1H_cA%ZDG7{W;`uh!GAF~CbnF$l4U9n5>?Z%ON zE&w`s=7dBW-XE6v1VJd%>t$TC@tSl!hm+I$!nkIu(q?3I-NwRR4BUaryZh%>gK?4) z;~o1hgc-S&w{B*vp;LkTb(rVlzp%4QT3U8~>S|CbwGVO5VE_^w#t;~-<-${-n4hGA zOPK11cJ6T`4_vD|@F`hsm*67^d+*8OWUFI~p%DHsv*UL0L6@#1wj1xnWi-qi(u(k$ z#zTAow?v}K-uXPAdKcGi2(Lxh(IHbU@_NM!q(gDr=@nuEeGPD$&ytmqg)8qL@6Vfu zf%ygIJZ&{?mR2U4@jPuwTIFBK#QCxDdw4$X^lR`#o)*Ju5gWMIn~z^FJ8pU4_We!G zQYl##@|{tnQ(@6Mk_O4-mG~Q*F0R=sEyDWx2_^OKUPOLivVA_beX;eTkK}_}v9h+1 znk2Mzc^W>eN4zG%+XjM?o}*2~ehNNvJqPoXjjnHX;z_Nou1NhtLS8nwnFkWNDleas z<#GVy*k238d4LyIRn=lWHNhk65V==UE=&GonFkX@4Xa1&?E7E=i8^dfZ2X?NBs~tq zdvVi8%CxiGZ${<;`)GGV_Jr5=%DW_5D6eB}Q*%F~3(yNqYIkiFtcu{LIeOOi7VKiD7J zBG~uFIBNIfD=KACT2WGFVP_!i10!SVccMHczseZ5cm*#=q8aEE%w50r7hSo2f__Cg z?+WN!wzM=tI3dnUNo0kmp!In74}v5QrvtezLF2 z)QZjmZ0=Cld;fMxhRkWpGAu56nTP1unbl?dZ?9Ra#eTkDS|PRFO6MJD+8Kv4xSj3SHx`~DsHC9ok)qTxLcZ* z#g~}bE68o&BNrLD1){M|_~oI!Slf^NYYP@o?UVOMH)TU4L5|@c?}nq%;r$bgI(#6sK%xOWuD}Uy z<{Hv`bb}-LR$KzVd|zEcWdq%pm%Ps>qU)aNBjSW$*3(MXNFIK5z@{{zWj(C<w|x z>UK05b|8P=Hr1yr2*IM@m4$t#uq+~09!OwFa1B{16MeR|)!#>iu8!%T>=Qii*D5LZ zy6_Ve)#mb*yFsv;>b&?5$bvj*O-)66|BJRHQ4X7BWUptIbC)N_SPFlO2KwHDWo7US zLO>yg6?PDO8y6_9jzFFq#4&BIv=_6Has0X-5)oHK)B#*H`zbU8Yx4CUOjd=x+P=Ii z9p8oT%4#Uc`_06C2$SXx zG7|uJJCNl4)=Ru;#<4e852$eTO2|DS*|+}4p0eoNHqs zSRxQPdnUk7MCgjxOj(+shp$6SBj$Ds*;Egy>f)4W;CTT!XqAdgv(dfiBK>-D_{xnJCCzohwNc50Pa zt?_a3lMXY?SKPmF_3cKo3Lko$MX6vIKnu8?i;YSF&oRddy43BtdJkWL2rhhFXn;<& zF#L1Sk&v;t>Uf#2!lWsEo8!a1=}^c%RWJs-NK#)3sEKIwPg=A*8#Gc95)Z*y{?B;9 zFw?2?twsc}aVEz9D_cFf)wUh*t0gy6c@YG{6QT^yRP!@UnO~QRYG9fG91;Hxq}7BR(_N;v5rj0qj<(#P(qjmZhaI$TsOmm0kCn zsQXDNzkYb2V`9xLC+9zpfc3w*G@hT~;3})0L zlA1@RbXqU)4umn%V?;%AXI5Muc_QevW$61pRuk{Bv2)l9T4|ramWkzA+{JkLzR8}4 zKo{pB=E2%VCWtV5x3I%H^A6s4ESZ)LoH|-}$a~#`_o7!(R^u@Z6T-ycuiVSHI@YRK zIM;vweBHspXdlMT)`>+ylmJO9TFWq$bp zcBQbKvZCv($FH)5@p}k6nX89!Xd~~uadzXePUSIaL{$ylLv=jm>x!YFH@j|O;n4n6 zCk)s6BuP}3s4#w9AHbu)(V{Cu0{jIN+8xAUM_C}ms zaN=*KB$Cz%*m%CBwR!=+#i;aY;FPGIw&$dd?5yVr=`KY)WPa7gmkenBQMkTJf^GZk zr7tjuAPdAmU%%5pLzB=pve~3B`!wrzPNzVOJ5=*K_D6gI3D$B4Sm z@gvFQ-ckw79YDY92N(SoZz#9ZH;j9E29jT9I6Q=c$rpRQJYs5iGw5C7jSX$x=g2<2 zXeQOf3(HR(RVho);lRMmIqbT^TrZkbmL1k!T(5lzGl2(`#BC|&IE+&{kOhk!Hvz4c zr#77VQfp#DT6QRoB9@&^%i<`~v;1LZQA#a2uauTt5j@IkoA1<)zMz$$-_9EV61gvI z|GtB_JD+;{MS25A!J-Y;jaP;q16@-SORqB++m7lZ#ry_@eWh=Uzas|A%fP~`ug`bS zM@l|9MlZ)wCt16wDA}%-{@j6&yX{*o^Neq5tL8)7oS8|I5t7BsJ5s*5WPDNlS5ScL z=ldCc@HE~V$O;)-fR{aauQr_if6OOxg(6pNygXhflURk9$t>|c<$lKz6@$Rw#4G#H zP#;UXfXc4NyD%{?3s4*d6OoB`fGTcTy??uDpAPel!iagB;>NU`n=1j#`%DObMoWI5 ze@Y*x39iSrs7`%STm|I)>;tup-n>al*Y{VxN285Xt2*^3Lqm&GJ=Tq>-(K~qcJ=AL z{t0ODKQ%wA!_mIJNYD{QZg&jmiNL-a1kYk#hf?piya-r!+9~mUbOL9tMwxr|Q;(0F zKR*V@%iv%BLtg&*DEg0p_V=9q?JVM7A8!Bud7L>ul__^n$Z#lh@)d#xs-bhYNc~~R Fe*-R;r9c1x literal 6339 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�+XW*m837 zMi!Uq1mv`rbFN{)mkWgbou9<6tOhj(`gR}BGgc9zUYwk4%1Hg`?#gH04Lu(y%j0%5 z50<~y;m-i`xj7v7!;H1YFk@Uw1Wk4is99Vy#_*l)$~Qmz9RviX`Fb?w1M|Vl6>&(_Q#ipft7(6 zO8Ya00_PU$Ft778C_&?6q7|5QeFY!*dEvE5xsl%|3TZ7L+Uo}SNqBzV-z$#24E1>U zN%@nmNzokxrJ*EDN;Sz+93O{pd-tM;I4D5li4=%Ixa&&Mj3| zUBZn?9j78gfg20D<&o(TGZb&Dn?b;-G(HUwfK>DgflXesni`i~56{3-WrCn_1RWe~ zH&Da^$y3_ZkygAz?+)Zop{K=Y8>&Cc5}|#vrf8&9UzyUUf&%tWF0$2=^>)zp6a%a` zN&Gp0>0w!4llX_WydUw>OIJ=?L!Z+6l6&QmvI~TnyD^tP$Tu9c*)X_5*uz3p-(?Q*d%za{YG2Y2xXd|8Od_8n(!gr>y{Qd}>lpAb zQCDcH+RPqZX7j77t{-AXp@Et#d&0zLjp}l21pnK2$>qP$V*7Sd< createWidget('dxSplitter', { width: '100%', height: 300, From 49bbb5391f8c740edd560c47764a7646bc6c6c2d Mon Sep 17 00:00:00 2001 From: pharret31 Date: Wed, 18 Mar 2026 12:13:11 +0100 Subject: [PATCH 3/4] rebase --- .../js/__internal/ui/splitter/splitter.ts | 185 +++++++++++++++++- .../DevExpress.ui.widgets/splitter.tests.js | 77 ++++++-- 2 files changed, 243 insertions(+), 19 deletions(-) diff --git a/packages/devextreme/js/__internal/ui/splitter/splitter.ts b/packages/devextreme/js/__internal/ui/splitter/splitter.ts index bcd463fb1c9d..9228b8f5f170 100644 --- a/packages/devextreme/js/__internal/ui/splitter/splitter.ts +++ b/packages/devextreme/js/__internal/ui/splitter/splitter.ts @@ -53,10 +53,12 @@ import { findIndexOfNextVisibleItem, findLastIndexOfNonCollapsedItem, findLastIndexOfVisibleItem, + findLastVisibleExpandedItemIndex, getElementSize, getNextLayout, isElementVisible, setFlexProp, + tryConvertToNumber, } from './utils/layout'; import { getDefaultLayout } from './utils/layout_default'; import { compareNumbersWithPrecision } from './utils/number_comparison'; @@ -128,12 +130,16 @@ class Splitter extends CollectionWidgetLiveUpdate { private _layout?: number[]; + private _idealLayout?: number[]; + private _currentLayout?: number[]; private _activeResizeHandleIndex?: number; private _collapseDirection?: CollapseExpandDirection; + private _initialPaneSizes: (string | number | undefined)[] = []; + private _itemRestrictions: PaneRestrictions[] = []; private _currentOnePxRatio?: number; @@ -236,13 +242,20 @@ class Splitter extends CollectionWidgetLiveUpdate { } _resizeHandler(): void { - if (this._shouldRecalculateLayout && this._isAttached() && this._isVisible()) { + if (!this._isAttached() || !this._isVisible()) { + return; + } + + if (this._shouldRecalculateLayout) { this._layout = this._getDefaultLayoutBasedOnSize(); + this._idealLayout = this._layout; this._applyStylesFromLayout(this._layout); this._updateItemSizes(); this._shouldRecalculateLayout = false; + } else { + this._dimensionChanged(); } } @@ -252,8 +265,11 @@ class Splitter extends CollectionWidgetLiveUpdate { this._updateResizeHandlesResizableState(); this._updateResizeHandlesCollapsibleState(); + this._initialPaneSizes = items.map((item: Item): string | number | undefined => item.size); + if (this._isVisible()) { this._layout = this._getDefaultLayoutBasedOnSize(); + this._idealLayout = this._layout; this._applyStylesFromLayout(this._layout); this._updateItemSizes(); @@ -537,6 +553,7 @@ class Splitter extends CollectionWidgetLiveUpdate { this._applyStylesFromLayout(newLayout); this._layout = newLayout; + this._idealLayout = newLayout; }, onResizeEnd: (e: ResizeEndEvent): void => { const { element, event } = e; @@ -663,6 +680,7 @@ class Splitter extends CollectionWidgetLiveUpdate { switch (property) { case 'size': this._layout = this._getDefaultLayoutBasedOnSize(item); + this._idealLayout = this._layout; this._applyStylesFromLayout(this.getLayout()); this._updateItemSizes(); @@ -671,6 +689,7 @@ class Splitter extends CollectionWidgetLiveUpdate { case 'minSize': case 'collapsedSize': this._layout = this._getDefaultLayoutBasedOnSize(); + this._idealLayout = this._layout; this._applyStylesFromLayout(this.getLayout()); this._updateItemSizes(); @@ -757,6 +776,7 @@ class Splitter extends CollectionWidgetLiveUpdate { } this._layout = newLayout; + this._idealLayout = this._layout; this._applyStylesFromLayout(this.getLayout()); this._updateItemSizes(); @@ -1105,12 +1125,171 @@ class Splitter extends CollectionWidgetLiveUpdate { } _dimensionChanged(): void { - this._layout = this._getDefaultLayoutBasedOnSize(); - this._applyStylesFromLayout(this._layout); + const idealLayout = this._idealLayout; + + if (!idealLayout || idealLayout.length === 0) { + return; + } + + const { orientation, items = [] } = this.option(); + const elementSize = getElementSize(this.$element(), orientation); + const handlesSize = this._getResizeHandlesSize(); + const availableSize = Math.max(0, elementSize - handlesSize); + + if (availableSize <= 0) { + this._layout = idealLayout.map((): number => 0); + this._applyStylesFromLayout(this._layout); + this._updateItemSizes(); + return; + } + + const idealPixels = idealLayout.map((ratio: number): number => (ratio / 100) * availableSize); + let remaining = 0; + const newPixels = idealPixels.map((px: number, index: number): number => { + const item = items[index]; + + if (!item || item.visible === false) { + remaining += px; + return 0; + } + + if (item.collapsed === true) { + const collapsedPx = tryConvertToNumber(item.collapsedSize, elementSize) ?? 0; + remaining += px - collapsedPx; + return collapsedPx; + } + + if (item.resizable === false) { + const originalSize = this._initialPaneSizes[index]; + + if (isDefined(originalSize)) { + const fixedPx = tryConvertToNumber(originalSize, elementSize) ?? px; + remaining += px - fixedPx; + return fixedPx; + } + } + + const minPx = tryConvertToNumber(item.minSize, elementSize) ?? 0; + const maxPx = tryConvertToNumber(item.maxSize, elementSize); + const clampedPx = this._getClampedPixelSize(px, minPx, maxPx); + + remaining += px - clampedPx; + return clampedPx; + }); + + this._distributeRemainingPixels(newPixels, remaining); + + this._layout = newPixels.map((px: number): number => (px / availableSize) * 100); + this._applyStylesFromLayout(this._layout); this._updateItemSizes(); } + _getEligiblePaneIndices( + pixels: number[], + remaining: number, + ): number[] { + const { items = [], orientation } = this.option(); + const elementSize = getElementSize(this.$element(), orientation); + const indices: number[] = []; + const direction = remaining > 0 ? 1 : -1; + + for (let index = 0; index < pixels.length; index += 1) { + const item = items[index]; + + if (!item || item.visible === false || item.collapsed === true + || (item.resizable === false && isDefined(this._initialPaneSizes[index]))) { + // skip + } else { + const minPx = tryConvertToNumber(item.minSize, elementSize) ?? 0; + const maxPx = tryConvertToNumber(item.maxSize, elementSize); + const clampedPx = this._getClampedPixelSize(pixels[index] + direction, minPx, maxPx); + + if (compareNumbersWithPrecision(clampedPx, pixels[index]) !== 0) { + indices.push(index); + } + } + } + + return indices; + } + + _distributeRemainingPixels( + pixels: number[], + initialRemaining: number, + ): void { + const { items = [] } = this.option(); + + let remaining = initialRemaining; + + while (compareNumbersWithPrecision(remaining, 0) !== 0) { + const eligiblePaneIndices = this._getEligiblePaneIndices(pixels, remaining); + + if (eligiblePaneIndices.length === 0) { + const fallback = findLastVisibleExpandedItemIndex(items); + + if (fallback !== -1) { + pixels[fallback] += remaining; + } + break; + } + + const share = remaining / eligiblePaneIndices.length; + const result = this._applyPixelShare(pixels, eligiblePaneIndices, share); + + remaining -= result.applied; + + if (!result.distributed) { + break; + } + } + } + + _applyPixelShare( + pixels: number[], + eligiblePaneIndices: number[], + share: number, + ): { applied: number; distributed: boolean } { + const { items = [], orientation } = this.option(); + const elementSize = getElementSize(this.$element(), orientation); + let applied = 0; + let distributed = false; + + eligiblePaneIndices.forEach((index: number): void => { + const item = items[index]; + const prev = pixels[index]; + const next = prev + share; + + const minPx = tryConvertToNumber(item?.minSize, elementSize) ?? 0; + const maxPx = tryConvertToNumber(item?.maxSize, elementSize); + const clampedPx = this._getClampedPixelSize(next, minPx, maxPx); + + const delta = clampedPx - prev; + + if (compareNumbersWithPrecision(delta, 0) !== 0) { + pixels[index] = clampedPx; + applied += delta; + distributed = true; + } + }); + + return { applied, distributed }; + } + + _getClampedPixelSize( + size: number, + minPx: number, + maxPx: number | undefined, + ): number { + let result = Math.max(size, minPx); + + if (isDefined(maxPx)) { + result = Math.min(result, maxPx); + } + + return result; + } + _optionChanged(args: OptionChanged): void { const { name, value } = args; diff --git a/packages/devextreme/testing/tests/DevExpress.ui.widgets/splitter.tests.js b/packages/devextreme/testing/tests/DevExpress.ui.widgets/splitter.tests.js index cc66c28968a3..7837c68be3ea 100644 --- a/packages/devextreme/testing/tests/DevExpress.ui.widgets/splitter.tests.js +++ b/packages/devextreme/testing/tests/DevExpress.ui.widgets/splitter.tests.js @@ -2112,18 +2112,18 @@ QUnit.module('Resizing', moduleConfig, () => { }); }); - QUnit.test(`non resizable panes shouldn't change their sizes after update splitter dimension, ${orientation} orientation`, function(assert) { + QUnit.test(`non resizable panes with restrictions shouldn't change their sizes after update splitter dimension, ${orientation} orientation`, function(assert) { this.reinit({ width: 1018, height: 1018, - dataSource: [{ }, { }, { }, { resizable: false, size: '300px' }], + dataSource: [{ }, { }, { }, { resizable: false, minSize: '300px', maxSize: '300px' }], orientation, }); this.instance.option(orientation === 'horizontal' ? 'width' : 'height', 700); - this.checkItemSizes([0, 166.664, 215.336, 300]); - this.assertLayout([0, 24.4375, 31.5742, 43.9883]); + this.checkItemSizes([104.602, 138.695, 138.703, 300]); + this.assertLayout(['15.3372', '20.3372', '20.3372', '43.9883']); }); }); @@ -3000,32 +3000,32 @@ QUnit.module('Resizing', moduleConfig, () => { initialWidth: 416, newWidth: 816, dataSource: [{ size: '200px', maxSize: '200px' }, { }, { }], - expectedLayout: ['25', '12.5', '62.5'], - expectedItemSizes: [200, 100, 500], + expectedLayout: ['25', '37.5', '37.5'], + expectedItemSizes: [200, 300, 300], }, { initialWidth: 816, newWidth: 416, dataSource: [{ size: '200px', minSize: '200px' }, { }, { }], - expectedLayout: ['50', '50', '0'], - expectedItemSizes: [200, 200, 0], + expectedLayout: ['50', '25', '25'], + expectedItemSizes: [200, 100, 100], }, { initialWidth: 416, newWidth: 816, dataSource: [{ }, { size: '200px', maxSize: '200px' }, { }], - expectedLayout: ['12.5', '25', '62.5'], - expectedItemSizes: [100, 200, 500], + expectedLayout: ['37.5', '25', '37.5'], + expectedItemSizes: [300, 200, 300], }, { - initialWidth: 416, - newWidth: 816, + initialWidth: 408, + newWidth: 808, dataSource: [{ size: '200px', minSize: '100px', maxSize: '300px' }, { }], - expectedLayout: ['24.7525', '75.2475'], - expectedItemSizes: [200, 608], + expectedLayout: ['37.5', '62.5'], + expectedItemSizes: [300, 500], }, { initialWidth: 208, newWidth: 808, dataSource: [{ size: '100px', maxSize: '200px' }, { }], - expectedLayout: ['12.5', '87.5'], - expectedItemSizes: [100, 700], + expectedLayout: ['25', '75'], + expectedItemSizes: [200, 600], }].forEach(({ initialWidth, newWidth, dataSource, expectedLayout, expectedItemSizes }) => { QUnit.test(`pane constraints should be respected after dimension change from ${initialWidth} to ${newWidth}, dataSource: ${JSON.stringify(dataSource)}`, function(assert) { this.reinit({ @@ -3040,6 +3040,51 @@ QUnit.module('Resizing', moduleConfig, () => { this.assertLayout(expectedLayout); }); }); + + QUnit.test('layout should be restored after shrinking and expanding back', function(assert) { + this.reinit({ + width: 416, + height: 408, + dataSource: [{ }, { }, { }], + }); + + this.assertLayout(['33.3333', '33.3333', '33.3333']); + + this.instance.option('width', 40); + this.instance.option('width', 416); + + this.assertLayout(['33.3333', '33.3333', '33.3333']); + }); + + QUnit.test('layout should be restored after shrinking and expanding with minSize', function(assert) { + this.reinit({ + width: 416, + height: 408, + dataSource: [{ minSize: '100px' }, { }, { }], + }); + + this.assertLayout(['33.3333', '33.3333', '33.3333']); + + this.instance.option('width', 40); + this.instance.option('width', 416); + + this.assertLayout(['33.3333', '33.3333', '33.3333']); + }); + + QUnit.test('layout should be restored after shrinking and expanding with maxSize', function(assert) { + this.reinit({ + width: 816, + height: 408, + dataSource: [{ size: '200px', maxSize: '200px' }, { }, { }], + }); + + this.assertLayout(['25', '37.5', '37.5']); + + this.instance.option('width', 40); + this.instance.option('width', 816); + + this.assertLayout(['25', '37.5', '37.5']); + }); }); QUnit.module('Initialization', moduleConfig, () => { From 9811119c4528eea7840c538296838d14a51fd18f Mon Sep 17 00:00:00 2001 From: pharret31 Date: Fri, 27 Mar 2026 12:01:52 +0100 Subject: [PATCH 4/4] add fallback option --- .../js/__internal/ui/splitter/splitter.ts | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/packages/devextreme/js/__internal/ui/splitter/splitter.ts b/packages/devextreme/js/__internal/ui/splitter/splitter.ts index 9228b8f5f170..09f293d09ae7 100644 --- a/packages/devextreme/js/__internal/ui/splitter/splitter.ts +++ b/packages/devextreme/js/__internal/ui/splitter/splitter.ts @@ -108,6 +108,7 @@ export interface Properties< keyof PublicProperties > { _renderQueue?: RenderQueueItem[]; + _ignoreSizeConstraints?: boolean; } interface PaneCache { @@ -165,6 +166,7 @@ class Splitter extends CollectionWidgetLiveUpdate { role: 'group', }, _renderQueue: undefined, + _ignoreSizeConstraints: false, }; } @@ -246,6 +248,9 @@ class Splitter extends CollectionWidgetLiveUpdate { return; } + // eslint-disable-next-line @typescript-eslint/naming-convention + const { _ignoreSizeConstraints } = this.option(); + if (this._shouldRecalculateLayout) { this._layout = this._getDefaultLayoutBasedOnSize(); this._idealLayout = this._layout; @@ -254,7 +259,7 @@ class Splitter extends CollectionWidgetLiveUpdate { this._updateItemSizes(); this._shouldRecalculateLayout = false; - } else { + } else if (!_ignoreSizeConstraints) { this._dimensionChanged(); } } @@ -1125,6 +1130,16 @@ class Splitter extends CollectionWidgetLiveUpdate { } _dimensionChanged(): void { + // eslint-disable-next-line @typescript-eslint/naming-convention + const { _ignoreSizeConstraints } = this.option(); + + if (_ignoreSizeConstraints) { + this._updateItemSizes(); + + this._layout = this._getDefaultLayoutBasedOnSize(); + return; + } + const idealLayout = this._idealLayout; if (!idealLayout || idealLayout.length === 0) {