@@ -34,10 +34,10 @@ export class GridStackEngine {
3434 private _float : boolean ;
3535 /** @internal */
3636 private _prevFloat : boolean ;
37- /** @internal */
37+ /** @internal cached layouts of difference column count so we can restore ack (eg 12 -> 1 -> 12) */
3838 private _layouts ?: Layout [ ] [ ] ; // maps column # to array of values nodes
39- /** @internal */
40- private _ignoreLayoutsNodeChange : boolean ;
39+ /** @internal true while we are resizing widgets during column resize to skip certain parts */
40+ private _inColumnResize : boolean ;
4141 /** @internal true if we have some items locked */
4242 private _hasLocked : boolean ;
4343 /** @internal unique global internal _id counter NOT starting at 0 */
@@ -273,6 +273,7 @@ export class GridStackEngine {
273273
274274 /** @internal called to top gravity pack the items back OR revert back to original Y positions when floating */
275275 private _packNodes ( ) : GridStackEngine {
276+ if ( this . batchMode ) { return ; }
276277 this . _sortNodes ( ) ; // first to last
277278
278279 if ( this . float ) {
@@ -346,6 +347,8 @@ export class GridStackEngine {
346347 /** part2 of preparing a node to fit inside our grid - checks for x,y from grid dimensions */
347348 public nodeBoundFix ( node : GridStackNode , resizing ?: boolean ) : GridStackNode {
348349
350+ let before = node . _orig || Utils . copyPos ( { } , node ) ;
351+
349352 if ( node . maxW ) { node . w = Math . min ( node . w , node . maxW ) ; }
350353 if ( node . maxH ) { node . h = Math . min ( node . h , node . maxH ) ; }
351354 if ( node . minW && node . minW <= this . column ) { node . w = Math . max ( node . w , node . minW ) ; }
@@ -354,7 +357,8 @@ export class GridStackEngine {
354357 if ( node . w > this . column ) {
355358 // if user loaded a larger than allowed widget for current # of columns,
356359 // remember it's full width so we can restore back (1 -> 12 column) #1655
357- if ( this . column < 12 ) {
360+ // IFF we're not in the middle of column resizing!
361+ if ( this . column < 12 && ! this . _inColumnResize ) {
358362 node . w = Math . min ( 12 , node . w ) ;
359363 this . cacheOneLayout ( node , 12 ) ;
360364 }
@@ -391,6 +395,10 @@ export class GridStackEngine {
391395 }
392396 }
393397
398+ if ( ! Utils . samePos ( node , before ) ) {
399+ node . _dirty = true ;
400+ }
401+
394402 return node ;
395403 }
396404
@@ -446,10 +454,11 @@ export class GridStackEngine {
446454
447455 /** call to add the given node to our list, fixing collision and re-packing */
448456 public addNode ( node : GridStackNode , triggerAddEvent = false ) : GridStackNode {
449- let dup : GridStackNode ;
450- if ( dup = this . nodes . find ( n => n . _id === node . _id ) ) return dup ; // prevent inserting twice! return it instead.
457+ let dup = this . nodes . find ( n => n . _id === node . _id ) ;
458+ if ( dup ) return dup ; // prevent inserting twice! return it instead.
451459
452- node = this . prepareNode ( node ) ;
460+ // skip prepareNode if we're in middle of column resize (not new) but do check for bounds!
461+ node = this . _inColumnResize ? this . nodeBoundFix ( node ) : this . prepareNode ( node ) ;
453462 delete node . _temporaryRemoved ;
454463 delete node . _removeDOM ;
455464
@@ -473,11 +482,10 @@ export class GridStackEngine {
473482 }
474483
475484 this . nodes . push ( node ) ;
476- triggerAddEvent && this . addedNodes . push ( node ) ;
485+ if ( triggerAddEvent ) { this . addedNodes . push ( node ) ; }
477486
478487 this . _fixCollisions ( node ) ;
479- this . _packNodes ( )
480- . _notify ( ) ;
488+ if ( ! this . batchMode ) { this . _packNodes ( ) . _notify ( ) ; }
481489 return node ;
482490 }
483491
@@ -696,7 +704,7 @@ export class GridStackEngine {
696704
697705 /** @internal called whenever a node is added or moved - updates the cached layouts */
698706 public layoutsNodesChange ( nodes : GridStackNode [ ] ) : GridStackEngine {
699- if ( ! this . _layouts || this . _ignoreLayoutsNodeChange ) return this ;
707+ if ( ! this . _layouts || this . _inColumnResize ) return this ;
700708 // remove smaller layouts - we will re-generate those on the fly... larger ones need to update
701709 this . _layouts . forEach ( ( layout , column ) => {
702710 if ( ! layout || column === this . column ) return this ;
@@ -705,12 +713,12 @@ export class GridStackEngine {
705713 }
706714 else {
707715 // we save the original x,y,w (h isn't cached) to see what actually changed to propagate better.
708- // Note: we don't need to check against out of bound scaling/moving as that will be done when using those cache values.
716+ // NOTE: we don't need to check against out of bound scaling/moving as that will be done when using those cache values. #1785
717+ let ratio = column / this . column ;
709718 nodes . forEach ( node => {
710719 if ( ! node . _orig ) return ; // didn't change (newly added ?)
711720 let n = layout . find ( l => l . _id === node . _id ) ;
712721 if ( ! n ) return ; // no cache for new nodes. Will use those values.
713- let ratio = column / this . column ;
714722 // Y changed, push down same amount
715723 // TODO: detect doing item 'swaps' will help instead of move (especially in 1 column mode)
716724 if ( node . y !== node . _orig . y ) {
@@ -736,55 +744,60 @@ export class GridStackEngine {
736744 * Note we store previous layouts (especially original ones) to make it possible to go
737745 * from say 12 -> 1 -> 12 and get back to where we were.
738746 *
739- * @param oldColumn previous number of columns
747+ * @param prevColumn previous number of columns
740748 * @param column new column number
741749 * @param nodes different sorted list (ex: DOM order) instead of current list
742750 * @param layout specify the type of re-layout that will happen (position, size, etc...).
743751 * Note: items will never be outside of the current column boundaries. default (moveScale). Ignored for 1 column
744752 */
745- public updateNodeWidths ( oldColumn : number , column : number , nodes : GridStackNode [ ] , layout : ColumnOptions = 'moveScale' ) : GridStackEngine {
746- if ( ! this . nodes . length || oldColumn === column ) return this ;
753+ public updateNodeWidths ( prevColumn : number , column : number , nodes : GridStackNode [ ] , layout : ColumnOptions = 'moveScale' ) : GridStackEngine {
754+ if ( ! this . nodes . length || ! column || prevColumn === column ) return this ;
747755
748756 // cache the current layout in case they want to go back (like 12 -> 1 -> 12) as it requires original data
749- this . cacheLayout ( this . nodes , oldColumn ) ;
757+ this . cacheLayout ( this . nodes , prevColumn ) ;
758+ this . batchUpdate ( ) ; // do this EARLY as it will call saveInitial() so we can detect where we started for _dirty and collision
759+ let newNodes : GridStackNode [ ] = [ ] ;
750760
751761 // if we're going to 1 column and using DOM order rather than default sorting, then generate that layout
752- if ( column === 1 && nodes && nodes . length ) {
762+ let domOrder = false ;
763+ if ( column === 1 && nodes ?. length ) {
764+ domOrder = true ;
753765 let top = 0 ;
754766 nodes . forEach ( n => {
755767 n . x = 0 ;
756768 n . w = 1 ;
757769 n . y = Math . max ( n . y , top ) ;
758770 top = n . y + n . h ;
759771 } ) ;
772+ newNodes = nodes ;
773+ nodes = [ ] ;
760774 } else {
761- nodes = Utils . sort ( this . nodes , - 1 , oldColumn ) ; // current column reverse sorting so we can insert last to front (limit collision)
762- }
763-
764- // see if we have cached previous layout.
765- let cacheNodes = this . _layouts [ column ] || [ ] ;
766- // if not AND we are going up in size start with the largest layout as down-scaling is more accurate
767- let lastIndex = this . _layouts . length - 1 ;
768- if ( cacheNodes . length === 0 && column > oldColumn && column < lastIndex ) {
769- cacheNodes = this . _layouts [ lastIndex ] || [ ] ;
770- if ( cacheNodes . length ) {
771- // pretend we came from that larger column by assigning those values as starting point
772- oldColumn = lastIndex ;
773- cacheNodes . forEach ( cacheNode => {
774- let j = nodes . findIndex ( n => n . _id === cacheNode . _id ) ;
775- if ( j !== - 1 ) {
775+ nodes = Utils . sort ( this . nodes , - 1 , prevColumn ) ; // current column reverse sorting so we can insert last to front (limit collision)
776+ }
777+
778+ // see if we have cached previous layout IFF we are going up in size (restore) otherwise always
779+ // generate next size down from where we are (looks more natural as you gradually size down).
780+ let cacheNodes : Layout [ ] = [ ] ;
781+ if ( column > prevColumn ) {
782+ cacheNodes = this . _layouts [ column ] || [ ] ;
783+ // ...if not, start with the largest layout (if not already there) as down-scaling is more accurate
784+ // by pretending we came from that larger column by assigning those values as starting point
785+ let lastIndex = this . _layouts . length - 1 ;
786+ if ( ! cacheNodes . length && prevColumn !== lastIndex && this . _layouts [ lastIndex ] ?. length ) {
787+ prevColumn = lastIndex ;
788+ this . _layouts [ lastIndex ] . forEach ( cacheNode => {
789+ let n = nodes . find ( n => n . _id === cacheNode . _id ) ;
790+ if ( n ) {
776791 // still current, use cache info positions
777- nodes [ j ] . x = cacheNode . x ;
778- nodes [ j ] . y = cacheNode . y ;
779- nodes [ j ] . w = cacheNode . w ;
792+ n . x = cacheNode . x ;
793+ n . y = cacheNode . y ;
794+ n . w = cacheNode . w ;
780795 }
781796 } ) ;
782- cacheNodes = [ ] ; // we still don't have new column cached data... will generate from larger one.
783797 }
784798 }
785799
786800 // if we found cache re-use those nodes that are still current
787- let newNodes : GridStackNode [ ] = [ ] ;
788801 cacheNodes . forEach ( cacheNode => {
789802 let j = nodes . findIndex ( n => n . _id === cacheNode . _id ) ;
790803 if ( j !== - 1 ) {
@@ -799,14 +812,15 @@ export class GridStackEngine {
799812 // ...and add any extra non-cached ones
800813 if ( nodes . length ) {
801814 if ( typeof layout === 'function' ) {
802- layout ( column , oldColumn , newNodes , nodes ) ;
803- } else {
804- let ratio = column / oldColumn ;
815+ layout ( column , prevColumn , newNodes , nodes ) ;
816+ } else if ( ! domOrder ) {
817+ let ratio = column / prevColumn ;
805818 let move = ( layout === 'move' || layout === 'moveScale' ) ;
806819 let scale = ( layout === 'scale' || layout === 'moveScale' ) ;
807820 nodes . forEach ( node => {
821+ // NOTE: x + w could be outside of the grid, but addNode() below will handle that
808822 node . x = ( column === 1 ? 0 : ( move ? Math . round ( node . x * ratio ) : Math . min ( node . x , column - 1 ) ) ) ;
809- node . w = ( ( column === 1 || oldColumn === 1 ) ? 1 :
823+ node . w = ( ( column === 1 || prevColumn === 1 ) ? 1 :
810824 scale ? ( Math . round ( node . w * ratio ) || 1 ) : ( Math . min ( node . w , column ) ) ) ;
811825 newNodes . push ( node ) ;
812826 } ) ;
@@ -816,15 +830,14 @@ export class GridStackEngine {
816830
817831 // finally re-layout them in reverse order (to get correct placement)
818832 newNodes = Utils . sort ( newNodes , - 1 , column ) ;
819- this . _ignoreLayoutsNodeChange = true ;
820- this . batchUpdate ( ) ;
821- this . nodes = [ ] ; // pretend we have no nodes to start with (we use same structures) to simplify layout
833+ this . _inColumnResize = true ; // prevent cache update
834+ this . nodes = [ ] ; // pretend we have no nodes to start with (add() will use same structures) to simplify layout
822835 newNodes . forEach ( node => {
823836 this . addNode ( node , false ) ; // 'false' for add event trigger
824- node . _dirty = true ; // force attr update
825- } , this ) ;
837+ delete node . _orig ; // make sure the commit doesn't try to restore things back to original
838+ } ) ;
826839 this . commit ( ) ;
827- delete this . _ignoreLayoutsNodeChange ;
840+ delete this . _inColumnResize ;
828841 return this ;
829842 }
830843
0 commit comments