@@ -128,6 +128,8 @@ export class GridStack {
128128 /** @internal */
129129 private _prevColumn : number ;
130130 /** @internal */
131+ private _ignoreLayoutsNodeChange : boolean ;
132+ /** @internal */
131133 public _gsEventHandler = { } ;
132134 /** @internal */
133135 private _styles : GridCSSStyleSheet ;
@@ -287,12 +289,10 @@ export class GridStack {
287289
288290 this . _updateContainerHeight ( ) ;
289291
290- this . onParentResize ( ) ;
291-
292292 this . _setupDragIn ( ) ;
293293 this . _setupRemoveDrop ( ) ;
294294 this . _setupAcceptWidget ( ) ;
295- this . _updateWindowResizeEvent ( ) ;
295+ this . _updateWindowResizeEvent ( ) ; // finally this may size us down to 1 column
296296 }
297297
298298 /**
@@ -307,7 +307,7 @@ export class GridStack {
307307 * grid.addWidget({width: 3, content: 'hello'});
308308 * grid.addWidget('<div class="grid-stack-item"><div class="grid-stack-item-content">hello</div></div>', {width: 3});
309309 *
310- * @param el html element, or string definition, or GridStackWidget (which can have content string as well) to add
310+ * @param el GridStackWidget (which can have content string as well), html element, or string definition to add
311311 * @param options widget position/size options (optional, and ignore if first param is already option) - see GridStackWidget
312312 */
313313 public addWidget ( els ?: GridStackWidget | GridStackElement , options ?: GridStackWidget ) : GridItemHTMLElement {
@@ -343,17 +343,22 @@ export class GridStack {
343343
344344 // Tempting to initialize the passed in opt with default and valid values, but this break knockout demos
345345 // as the actual value are filled in when _prepareElement() calls el.getAttribute('data-gs-xyz) before adding the node.
346- if ( options ) {
347- options = { ...options } ; // make a copy before we modify in case caller re-uses it
348- // make sure we load any DOM attributes that are not specified in passed in options (which override)
349- let domAttr = this . _readAttr ( el ) ;
350- Utils . defaults ( options , domAttr ) ;
351- this . engine . prepareNode ( options ) ;
352- this . _writeAttr ( el , options ) ;
353- }
346+ // So make sure we load any DOM attributes that are not specified in passed in options (which override)
347+ let domAttr = this . _readAttr ( el ) ;
348+ options = { ...( options || { } ) } ; // make a copy before we modify in case caller re-uses it
349+ Utils . defaults ( options , domAttr ) ;
350+ this . engine . prepareNode ( options ) ;
351+ this . _writeAttr ( el , options ) ;
354352
355353 this . el . appendChild ( el ) ;
356- return this . makeWidget ( el ) ;
354+
355+ // similar to makeWidget() that doesn't read attr again and worse re-create a new node and loose any _id
356+ this . _prepareElement ( el , true , options ) ;
357+ this . _updateContainerHeight ( ) ;
358+ this . _triggerAddEvent ( ) ;
359+ this . _triggerChangeEvent ( ) ;
360+
361+ return el ;
357362 }
358363
359364 /** saves the current layout returning a list of widgets for serialization */
@@ -385,6 +390,14 @@ export class GridStack {
385390 **/
386391 public load ( layout : GridStackWidget [ ] , addAndRemove : boolean | ( ( w : GridStackWidget , add : boolean ) => void ) = true ) : GridStack {
387392 let items = GridStack . Utils . sort ( layout ) ;
393+
394+ // if we're loading a layout into 1 column (_prevColumn is set only when going to 1) and items don't fit, make sure to save
395+ // the original wanted layout so we can scale back up correctly #1471
396+ if ( this . _prevColumn && this . _prevColumn !== this . opts . column && items . some ( n => ( n . x + n . width ) > this . opts . column ) ) {
397+ this . _ignoreLayoutsNodeChange = true ; // skip layout update
398+ this . engine . cacheLayout ( items , this . _prevColumn , true ) ;
399+ }
400+
388401 let removed : GridStackNode [ ] = [ ] ;
389402 this . batchUpdate ( ) ;
390403 // see if any items are missing from new layout and need to be removed first
@@ -404,7 +417,7 @@ export class GridStack {
404417 }
405418 // now add/update the widgets
406419 items . forEach ( w => {
407- let item = this . engine . nodes . find ( n => n . id === w . id ) ;
420+ let item = ( w . id || w . id === 0 ) ? this . engine . nodes . find ( n => n . id === w . id ) : undefined ;
408421 if ( item ) {
409422 this . update ( item . el , w . x , w . y , w . width , w . height ) ; // TODO: full update
410423 } else if ( addAndRemove ) {
@@ -417,6 +430,10 @@ export class GridStack {
417430 } ) ;
418431 this . engine . removedNodes = removed ;
419432 this . commit ( ) ;
433+
434+ // after commit, clear that flag
435+ delete this . _ignoreLayoutsNodeChange ;
436+
420437 return this ;
421438 }
422439
@@ -531,7 +548,10 @@ export class GridStack {
531548 this . engine . updateNodeWidths ( oldColumn , column , domNodes , layout ) ;
532549
533550 // and trigger our event last...
534- this . _triggerChangeEvent ( true ) ; // skip layout update
551+ this . _ignoreLayoutsNodeChange = true ; // skip layout update
552+ this . _triggerChangeEvent ( ) ;
553+ delete this . _ignoreLayoutsNodeChange ;
554+
535555 return this ;
536556 }
537557
@@ -1007,11 +1027,11 @@ export class GridStack {
10071027 }
10081028
10091029 /** @internal */
1010- private _triggerChangeEvent ( skipLayoutChange ?: boolean ) : GridStack {
1030+ private _triggerChangeEvent ( ) : GridStack {
10111031 if ( this . engine . batchMode ) { return this ; }
10121032 let elements = this . engine . getDirtyNodes ( true ) ; // verify they really changed
10131033 if ( elements && elements . length ) {
1014- if ( ! skipLayoutChange ) {
1034+ if ( ! this . _ignoreLayoutsNodeChange ) {
10151035 this . engine . layoutsNodesChange ( elements ) ;
10161036 }
10171037 this . _triggerEvent ( 'change' , elements ) ;
@@ -1024,7 +1044,9 @@ export class GridStack {
10241044 private _triggerAddEvent ( ) : GridStack {
10251045 if ( this . engine . batchMode ) { return this }
10261046 if ( this . engine . addedNodes && this . engine . addedNodes . length > 0 ) {
1027- this . engine . layoutsNodesChange ( this . engine . addedNodes ) ;
1047+ if ( ! this . _ignoreLayoutsNodeChange ) {
1048+ this . engine . layoutsNodesChange ( this . engine . addedNodes ) ;
1049+ }
10281050 // prevent added nodes from also triggering 'change' event (which is called next)
10291051 this . engine . addedNodes . forEach ( n => { delete n . _dirty ; } ) ;
10301052 this . _triggerEvent ( 'added' , this . engine . addedNodes ) ;
@@ -1157,12 +1179,20 @@ export class GridStack {
11571179
11581180
11591181 /** @internal */
1160- private _prepareElement ( el : GridItemHTMLElement , triggerAddEvent = false ) : GridStack {
1161- el . classList . add ( this . opts . itemClass ) ;
1162- let node = this . _readAttr ( el , { el : el , grid : this } ) ;
1163- node = this . engine . addNode ( node , triggerAddEvent ) ;
1182+ private _prepareElement ( el : GridItemHTMLElement , triggerAddEvent = false , node ?: GridStackNode ) : GridStack {
1183+ if ( ! node ) {
1184+ el . classList . add ( this . opts . itemClass ) ;
1185+ node = this . _readAttr ( el ) ;
1186+ }
11641187 el . gridstackNode = node ;
1165- this . _writeAttr ( el , node ) ;
1188+ node . el = el ;
1189+ node . grid = this ;
1190+ let copy = { ...node } ;
1191+ node = this . engine . addNode ( node , triggerAddEvent ) ;
1192+ // write node attr back in case there was collision or we have to fix bad values during addNode()
1193+ if ( ! Utils . same ( node , copy ) ) {
1194+ this . _writeAttr ( el , node ) ;
1195+ }
11661196 this . _prepareDragDropByNode ( node ) ;
11671197 return this ;
11681198 }
@@ -1231,6 +1261,14 @@ export class GridStack {
12311261 node . resizeHandles = el . getAttribute ( 'data-gs-resize-handles' ) ;
12321262 node . id = el . getAttribute ( 'data-gs-id' ) ;
12331263
1264+ // remove any key not found (null or false which is default)
1265+ for ( const key in node ) {
1266+ if ( ! node . hasOwnProperty ( key ) ) { return ; }
1267+ if ( node [ key ] === null || node [ key ] === false ) {
1268+ delete node [ key ] ;
1269+ }
1270+ }
1271+
12341272 return node ;
12351273 }
12361274
@@ -1305,6 +1343,7 @@ export class GridStack {
13051343 if ( workTodo && ! forceRemove && ! this . opts . _isNested && ! this . _windowResizeBind ) {
13061344 this . _windowResizeBind = this . onParentResize . bind ( this ) ; // so we can properly remove later
13071345 window . addEventListener ( 'resize' , this . _windowResizeBind ) ;
1346+ this . onParentResize ( ) ; // initially call it once...
13081347 } else if ( ( forceRemove || ! workTodo ) && this . _windowResizeBind ) {
13091348 window . removeEventListener ( 'resize' , this . _windowResizeBind ) ;
13101349 delete this . _windowResizeBind ; // remove link to us so we can free
0 commit comments