|
469 | 469 | }; |
470 | 470 |
|
471 | 471 | GridStackEngine.prototype.addNode = function(node, triggerAddEvent) { |
| 472 | + var prev = {x: node.x, y: node.y, width: node.width, height: node.height}; |
| 473 | + |
472 | 474 | node = this._prepareNode(node); |
473 | 475 |
|
474 | 476 | if (node.maxWidth !== undefined) { node.width = Math.min(node.width, node.maxWidth); } |
475 | 477 | if (node.maxHeight !== undefined) { node.height = Math.min(node.height, node.maxHeight); } |
476 | 478 | if (node.minWidth !== undefined) { node.width = Math.max(node.width, node.minWidth); } |
477 | 479 | if (node.minHeight !== undefined) { node.height = Math.max(node.height, node.minHeight); } |
478 | 480 |
|
479 | | - node._id = ++idSeq; |
| 481 | + node._id = node._id || ++idSeq; |
480 | 482 | // node._dirty = true; will be addEvent instead, unless it changes below... |
481 | 483 |
|
482 | 484 | if (node.autoPosition) { |
|
489 | 491 | continue; |
490 | 492 | } |
491 | 493 | if (!this.nodes.find(Utils._isAddNodeIntercepted, {x: x, y: y, node: node})) { |
| 494 | + node._dirty = (node.x !== x || node.y !== y); |
492 | 495 | node.x = x; |
493 | 496 | node.y = y; |
494 | 497 | delete node.autoPosition; // found our slot |
495 | | - node._dirty = (node.x !== x || node.y !== y); |
496 | 498 | break; |
497 | 499 | } |
498 | 500 | } |
|
502 | 504 | if (triggerAddEvent) { |
503 | 505 | this._addedNodes.push(node); |
504 | 506 | } |
| 507 | + // use single equal as they come as string/undefined but end as number.... |
| 508 | + if (!node._dirty && (prev.x != node.x || prev.y != node.y || prev.width != node.width || prev.height != node.height)) { |
| 509 | + node._dirty = true; |
| 510 | + } |
505 | 511 |
|
506 | 512 | this._fixCollisions(node); |
507 | 513 | this._packNodes(); |
|
1697 | 1703 | }); |
1698 | 1704 | }; |
1699 | 1705 |
|
| 1706 | + /** |
| 1707 | + * relayout grid items to reclaim any empty space |
| 1708 | + */ |
| 1709 | + GridStack.prototype.compact = function() { |
| 1710 | + if (this.grid.nodes.length === 0) { return; } |
| 1711 | + this.batchUpdate(); |
| 1712 | + this.grid._sortNodes(); |
| 1713 | + var nodes = this.grid.nodes; |
| 1714 | + this.grid.nodes = []; // pretend we have no nodes to conflict layout to start with... |
| 1715 | + nodes.forEach(function(n) { |
| 1716 | + if (!n.noMove && !n.locked) { |
| 1717 | + n.autoPosition = true; |
| 1718 | + } |
| 1719 | + this.grid.addNode(n, false); // 'false' for add event trigger |
| 1720 | + }, this); |
| 1721 | + this.commit(); |
| 1722 | + }; |
| 1723 | + |
1700 | 1724 | GridStack.prototype.verticalMargin = function(val, noUpdate) { |
1701 | 1725 | if (val === undefined) { |
1702 | 1726 | return this.opts.verticalMargin; |
|
1809 | 1833 | // |
1810 | 1834 | // now update the nodes positions, using the original ones with new ratio |
1811 | 1835 | // |
| 1836 | + |
1812 | 1837 | if (doNotPropagate === true || this.grid.nodes.length === 0) { return; } |
1813 | 1838 | var nodes = Utils.sort(this.grid.nodes, -1, oldColumn); // current column reverse sorting so we can insert last to front (limit collision) |
1814 | 1839 |
|
1815 | 1840 | // cache the current layout in case they want to go back (like 12 -> 1 -> 12) as it requires original data |
1816 | 1841 | var copy = [nodes.length]; |
1817 | | - nodes.forEach(function(n, i) {copy[i] = Utils.clone(n)}); // clone to preserve _id that gets reset during removal, and changing x,y,w,h live objects |
1818 | | - this.grid._layouts = this.grid._layouts || {}; |
| 1842 | + nodes.forEach(function(n, i) {copy[i] = {x: n.x, y: n.y, width: n.width, _id: n._id}}); // only thing we use change is x,y,w and need id to find it back |
| 1843 | + this.grid._layouts = this.grid._layouts || []; // use array to find larger quick |
1819 | 1844 | this.grid._layouts[oldColumn] = copy; |
1820 | 1845 |
|
1821 | | - // see if we have cached prev values and if so re-use those nodes that are still current... |
1822 | | - var newNodes = []; |
| 1846 | + // see if we have cached previous layout. if NOT and we are going up in size (up-sampling) start with the largest layout we have (down-sampling) instead |
| 1847 | + var lastIndex = this.grid._layouts.length - 1; |
1823 | 1848 | var cacheNodes = this.grid._layouts[column] || []; |
| 1849 | + if (cacheNodes.length === 0 && column > oldColumn && lastIndex > column) { |
| 1850 | + cacheNodes = this.grid._layouts[lastIndex] || []; |
| 1851 | + if (cacheNodes.length) { |
| 1852 | + // pretend we came from that larger column by assigning those values at starting point) |
| 1853 | + oldColumn = lastIndex; |
| 1854 | + cacheNodes.forEach(function(cacheNode) { |
| 1855 | + var j = nodes.findIndex(function(n) {return n && n._id === cacheNode._id}); |
| 1856 | + if (j !== -1) { |
| 1857 | + // still current, use cache info positions |
| 1858 | + nodes[j].x = cacheNode.x; |
| 1859 | + nodes[j].y = cacheNode.y; |
| 1860 | + nodes[j].width = cacheNode.width; |
| 1861 | + } |
| 1862 | + }); |
| 1863 | + cacheNodes = []; // we still don't have new column cached data... will generate from larger one. |
| 1864 | + } |
| 1865 | + } |
| 1866 | + |
| 1867 | + // if we found cache re-use those nodes that are still current |
| 1868 | + var newNodes = []; |
1824 | 1869 | cacheNodes.forEach(function(cacheNode) { |
1825 | 1870 | var j = nodes.findIndex(function(n) {return n && n._id === cacheNode._id}); |
1826 | 1871 | if (j !== -1) { |
1827 | | - newNodes.push(cacheNode); // still current, use cache info |
1828 | | - nodes[j] = null; |
| 1872 | + // still current, use cache info positions |
| 1873 | + nodes[j].x = cacheNode.x; |
| 1874 | + nodes[j].y = cacheNode.y; |
| 1875 | + nodes[j].width = cacheNode.width; |
| 1876 | + newNodes.push(nodes[j]); |
| 1877 | + nodes[j] = null; // erase it so we know what's left |
1829 | 1878 | } |
1830 | 1879 | }); |
1831 | 1880 | // ...and add any extra non-cached ones |
1832 | 1881 | var ratio = column / oldColumn; |
1833 | 1882 | nodes.forEach(function(node) { |
1834 | 1883 | if (!node) return; |
1835 | | - newNodes.push($.extend({}, node, {x: Math.round(node.x * ratio), width: Math.round(node.width * ratio) || 1})); |
| 1884 | + node.x = Math.round(node.x * ratio); |
| 1885 | + node.width = Math.round(node.width * ratio) || 1; |
| 1886 | + newNodes.push(node); |
1836 | 1887 | }); |
1837 | 1888 | newNodes = Utils.sort(newNodes, -1, column); |
1838 | 1889 |
|
1839 | | - // now temporary remove the existing gs info and add them from last to make sure we insert them where needed |
1840 | | - // (batch mode will set float=true so we can position anywhere and do gravity relayout after) |
| 1890 | + // finally relayout them in reverse order (to get correct placement) |
1841 | 1891 | this.batchUpdate(); |
1842 | | - this.grid.removeAll(false); // 'false' = leave DOm elements behind |
| 1892 | + this.grid.nodes = []; // pretend we have no nodes to start with (we use same structures) to simplify layout |
1843 | 1893 | newNodes.forEach(function(node) { |
1844 | | - var newNode = this.addWidget(node.el, node).data('_gridstack_node'); |
1845 | | - newNode._id = node._id; // keep same ID so we can re-use caches |
1846 | | - newNode._dirty = true; |
| 1894 | + this.grid.addNode(node, false); // 'false' for add event trigger |
| 1895 | + node._dirty = true; // force attr update |
1847 | 1896 | }, this); |
1848 | | - this.grid._removedNodes = []; // prevent add/remove from being called (kept DOM) only change event |
1849 | | - this.grid._addedNodes = []; |
1850 | 1897 | this.commit(); |
1851 | 1898 | }; |
1852 | 1899 |
|
|
0 commit comments