Skip to content

Commit 83e11d2

Browse files
committed
nested grid column:'auto' option
* partial fix #1009 * we now have a new column:'auto' option which is used for nested grids to column size themselves to match their container (same # of column) This make sure outside and inside items stay the same size * this will eventually be required when creating grids on the fly dynamically (as good default) * updated nested.html to showcase this
1 parent f280a1c commit 83e11d2

File tree

5 files changed

+85
-42
lines changed

5 files changed

+85
-42
lines changed

demo/nested.html

Lines changed: 19 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,13 @@
99
<link rel="stylesheet" href="../dist/gridstack-extra.min.css"/>
1010
<script src="../dist/gridstack-h5.js"></script>
1111
<style type="text/css">
12-
.grid-stack .grid-stack {
13-
background: rgba(255, 255, 255, 0.3);
12+
/* make nested grids have slightly darker bg */
13+
.grid-stack.grid-stack-nested {
14+
background: #e4e4c1;
1415
}
15-
.grid-stack .grid-stack .grid-stack-item-content {
16-
background: lightpink;
16+
/* make nested grid take almost all space (need some to tell them apart) so items inside can have similar to external size+margin */
17+
.grid-stack > .grid-stack-item.grid-stack-nested > .grid-stack-item-content {
18+
inset: 2px;
1719
}
1820
/* make nested grid take entire item content */
1921
.grid-stack-item-content .grid-stack {
@@ -25,11 +27,12 @@
2527
<body>
2628
<div class="container-fluid">
2729
<h1>Nested grids demo</h1>
28-
<p>This example uses new v3.1 API to load the entire nested grid from JSON, and shows dragging between nested grid items (pink) vs dragging higher items (green)</p>
29-
<p>Note: HTML5 release doesn't yet support 'dragOut:false' constrain so use JQ version if you need that (nested 2 case).</p>
30+
<p>This example shows v5.x dragging between nested grids (dark yellow) and parent grid (bright yellow.)<br>
31+
Uses v3.1 API to load the entire nested grid from JSON.<br>
32+
Nested grids uses new <b>column:'auto'</b> to keep items same size during resize.</p>
3033
<a class="btn btn-primary" onClick="addNested()" href="#">Add Widget</a>
31-
<a class="btn btn-primary" onClick="addNewWidget('.nested1')" href="#">Add Widget Grid1</a>
32-
<a class="btn btn-primary" onClick="addNewWidget('.nested2')" href="#">Add Widget Grid2</a>
34+
<a class="btn btn-primary" onClick="addNewWidget('.sub1')" href="#">Add Widget Grid1</a>
35+
<a class="btn btn-primary" onClick="addNewWidget('.sub2')" href="#">Add Widget Grid2</a>
3336
<span>entire save/re-create:</span>
3437
<a class="btn btn-primary" onClick="save()" href="#">Save</a>
3538
<a class="btn btn-primary" onClick="destroy()" href="#">Destroy</a>
@@ -49,29 +52,29 @@ <h1>Nested grids demo</h1>
4952
let count = 0;
5053
[...sub1, ...sub2].forEach(d => d.content = String(count++));
5154
let subOptions = {
52-
cellHeight: 30,
53-
column: 4, // make sure to include gridstack-extra.min.css
55+
cellHeight: 50,
56+
column: 'auto', // size to match container. make sure to include gridstack-extra.min.css
5457
acceptWidgets: true, // will accept .grid-stack-item by default
55-
minWidth: 300, // min to go 1 column mode (much smaller than default)
56-
margin: 2
58+
margin: 5,
5759
};
5860
let options = { // main grid options
59-
cellHeight: 70,
61+
cellHeight: 50,
62+
margin: 5,
6063
minRow: 2, // don't collapse when empty
6164
acceptWidgets: true,
6265
id: 'main',
6366
children: [
6467
{y:0, content: 'regular item'},
65-
{x:1, w:4, h:4, subGrid: {children: sub1, dragOut: true, id: 'sub1', ...subOptions}},
66-
{x:5, w:4, h:4, subGrid: {children: sub2, id: 'sub2', ...subOptions}},
68+
{x:1, w:4, h:4, subGrid: {children: sub1, dragOut: true, class: 'sub1', ...subOptions}},
69+
{x:5, w:3, h:4, subGrid: {children: sub2, class: 'sub2', ...subOptions}},
6770
]
6871
};
6972

7073
// create and load it all from JSON above
7174
let grid = GridStack.addGrid(document.querySelector('.container-fluid'), options);
7275

7376
addNested = function() {
74-
grid.addWidget({x:0, y:0, content:"new item"});
77+
grid.addWidget({x:0, y:100, content:"new item"});
7578
}
7679

7780
addNewWidget = function(selector) {

doc/CHANGES.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ Change log
6868

6969
## 4.4.1-dev (TBD)
7070
* add [#992](https://github.com/gridstack/gridstack.js/issues/992) support dragging into and out of nested grids from parents! Thank you [@arclogos132](https://github.com/arclogos132) for sponsoring it.
71+
* add [#1910](https://github.com/gridstack/gridstack.js/pull/1910) new `column:'auto'` option to size nested grids to their parent grid item column count, keeping items the same size inside and outside. Thank you [@arclogos132](https://github.com/arclogos132) for also sponsoring it.
7172
* fix [#1902](https://github.com/gridstack/gridstack.js/pull/1902) nested.html: dragging between sub-grids show items clipped
7273
* fix [#1558](https://github.com/gridstack/gridstack.js/issues/1558) dragging between vertical grids causes too much growth, not follow mouse.
7374

doc/README.md

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ gridstack.js API
3434
- [`cellHeight(val: number, update = true)`](#cellheightval-number-update--true)
3535
- [`cellWidth()`](#cellwidth)
3636
- [`commit()`](#commit)
37-
- [`column(column: number, layout: ColumnOptions = 'moveScale')`](#columncolumn-number-layout-columnoptions--movescale)
37+
- [`column(column: number | 'auto', layout: ColumnOptions = 'moveScale')`](#columncolumn-number--auto-layout-columnoptions--movescale)
3838
- [`destroy([removeDOM])`](#destroyremovedom)
3939
- [`disable()`](#disable)
4040
- [`enable()`](#enable)
@@ -43,6 +43,7 @@ gridstack.js API
4343
- [`float(val?)`](#floatval)
4444
- [`getCellHeight()`](#getcellheight)
4545
- [`getCellFromPixel(position[, useOffset])`](#getcellfrompixelposition-useoffset)
46+
- [`getColumn(): number`](#getcolumn-number)
4647
- [`getGridItems(): GridItemHTMLElement[]`](#getgriditems-griditemhtmlelement)
4748
- [`getMargin()`](#getmargin)
4849
- [`isAreaEmpty(x, y, width, height)`](#isareaemptyx-y-width-height)
@@ -365,14 +366,14 @@ Gets current cell width (grid width / # of columns).
365366

366367
Ends batch updates. Updates DOM nodes. You must call it after `batchUpdate()`.
367368

368-
### `column(column: number, layout: ColumnOptions = 'moveScale')`
369+
### `column(column: number | 'auto', layout: ColumnOptions = 'moveScale')`
369370

370-
set/get the number of columns in the grid. Will update existing widgets to conform to new number of columns,
371+
set the number of columns in the grid. Will update existing widgets to conform to new number of columns,
371372
as well as cache the original layout so you can revert back to previous positions without loss.
372-
Requires `gridstack-extra.css` or `gridstack-extra.min.css` for [2-11],
373+
Requires `gridstack-extra.css` (or minimized version) for [2-11],
373374
else you will need to generate correct CSS (see https://github.com/gridstack/gridstack.js#change-grid-columns)
374375

375-
- `column` - Integer > 0 (default 12), if missing it will return the current count instead.
376+
- `column` - Integer > 0 (default 12), or 'auto' for nested grids to size themselves to the parent grid container (to make su-items the same size inside and outside)
376377
- `layout` - specify the type of re-layout that will happen (position, size, etc...).
377378
Note: items will never be outside of the current column boundaries. default ('moveScale'). Ignored for 1 column.
378379
Possible values: 'moveScale' | 'move' | 'scale' | 'none' | (column: number, oldColumn: number, nodes: GridStackNode[], oldNodes: GridStackNode[]) => void.
@@ -445,6 +446,10 @@ Parameters :
445446

446447
Returns an object with properties `x` and `y` i.e. the column and row in the grid.
447448

449+
### `getColumn(): number`
450+
451+
returns the number of columns in the grid.
452+
448453
### `getGridItems(): GridItemHTMLElement[]`
449454

450455
Return list of GridItem HTML elements (excluding temporary placeholder) in DOM order, wether they are node items yet or not (looks by class)

src/gridstack.ts

Lines changed: 50 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -228,6 +228,8 @@ export class GridStack {
228228
private _insertNotAppend: boolean;
229229
/** @internal extra row added when dragging at the bottom of the grid */
230230
private _extraDragRow = 0;
231+
/** @internal true if nested grid should get column count from our width */
232+
private _autoColumn?: boolean;
231233

232234
/**
233235
* Construct a grid item from the given element and options
@@ -245,6 +247,11 @@ export class GridStack {
245247
}
246248
let rowAttr = Utils.toNumber(el.getAttribute('gs-row'));
247249

250+
// flag only valid in sub-grids (handled by parent, not here)
251+
if (opts.column === 'auto') {
252+
delete opts.column;
253+
}
254+
248255
// elements attributes override any passed options (like CSS style) - merge the two together
249256
let defaults: GridStackOptions = {...Utils.cloneDeep(GridDefaults),
250257
column: Utils.toNumber(el.getAttribute('gs-column')) || 12,
@@ -276,7 +283,7 @@ export class GridStack {
276283

277284
// Now check if we're loading into 1 column mode FIRST so we don't do un-necessary work (like cellHeight = width / 12 then go 1 column)
278285
if (this.opts.column !== 1 && !this.opts.disableOneColumnMode && this._widthOrContainer() <= this.opts.minWidth) {
279-
this._prevColumn = this.opts.column;
286+
this._prevColumn = this.getColumn();
280287
this.opts.column = 1;
281288
}
282289

@@ -315,7 +322,7 @@ export class GridStack {
315322
this._setStaticClass();
316323

317324
this.engine = new GridStackEngine({
318-
column: this.opts.column,
325+
column: this.getColumn(),
319326
float: this.opts.float,
320327
maxRow: this.opts.maxRow,
321328
onChange: (cbNodes) => {
@@ -344,7 +351,7 @@ export class GridStack {
344351
elements.push({
345352
el,
346353
// if x,y are missing (autoPosition) add them to end of list - but keep their respective DOM order
347-
i: (Number.isNaN(x) ? 1000 : x) + (Number.isNaN(y) ? 1000 : y) * this.opts.column
354+
i: (Number.isNaN(x) ? 1000 : x) + (Number.isNaN(y) ? 1000 : y) * this.getColumn()
348355
});
349356
});
350357
elements.sort((a, b) => a.i - b.i).forEach(e => this._prepareElement(e.el));
@@ -435,8 +442,17 @@ export class GridStack {
435442

436443
// check if nested grid definition is present
437444
if (node.subGrid && !(node.subGrid as GridStack).el) { // see if there is a sub-grid to create too
445+
// if column special case it set, remember that flag and set default
446+
let autoColumn: boolean;
447+
let ops = node.subGrid as GridStackOptions;
448+
if (ops.column === 'auto') {
449+
ops.column = node.w;
450+
ops.disableOneColumnMode = true; // driven by parent
451+
autoColumn = true;
452+
}
438453
let content = node.el.querySelector('.grid-stack-item-content') as HTMLElement;
439454
node.subGrid = GridStack.addGrid(content, node.subGrid as GridStackOptions);
455+
if (autoColumn) { node.subGrid._autoColumn = true; }
440456
}
441457

442458
this._triggerAddEvent();
@@ -483,7 +499,13 @@ export class GridStack {
483499
delete o.marginTop; delete o.marginRight; delete o.marginBottom; delete o.marginLeft;
484500
}
485501
if (o.rtl === (this.el.style.direction === 'rtl')) { o.rtl = 'auto' }
486-
if (this._isAutoCellHeight) { o.cellHeight = 'auto' }
502+
if (this._isAutoCellHeight) {
503+
o.cellHeight = 'auto'
504+
}
505+
if (this._autoColumn) {
506+
o.column = 'auto';
507+
delete o.disableOneColumnMode;
508+
}
487509
Utils.removeInternalAndSame(o, GridDefaults);
488510
o.children = list;
489511
return o;
@@ -503,7 +525,7 @@ export class GridStack {
503525
* see http://gridstackjs.com/demo/serialization.html
504526
**/
505527
public load(layout: GridStackWidget[], addAndRemove: boolean | ((g: GridStack, w: GridStackWidget, add: boolean) => GridItemHTMLElement) = true): GridStack {
506-
let items = GridStack.Utils.sort([...layout], -1, this._prevColumn || this.opts.column); // make copy before we mod/sort
528+
let items = GridStack.Utils.sort([...layout], -1, this._prevColumn || this.getColumn()); // make copy before we mod/sort
507529
this._insertNotAppend = true; // since create in reverse order...
508530

509531
// 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
@@ -633,7 +655,7 @@ export class GridStack {
633655

634656
/** Gets current cell width. */
635657
public cellWidth(): number {
636-
return this._widthOrContainer() / this.opts.column;
658+
return this._widthOrContainer() / this.getColumn();
637659
}
638660
/** return our expected width (or parent) for 1 column check */
639661
private _widthOrContainer(): number {
@@ -671,7 +693,7 @@ export class GridStack {
671693
*/
672694
public column(column: number, layout: ColumnOptions = 'moveScale'): GridStack {
673695
if (column < 1 || this.opts.column === column) return this;
674-
let oldColumn = this.opts.column;
696+
let oldColumn = this.getColumn();
675697

676698
// if we go into 1 column mode (which happens if we're sized less than minW unless disableOneColumnMode is on)
677699
// then remember the original columns so we can restore.
@@ -709,7 +731,7 @@ export class GridStack {
709731
* get the number of columns in the grid (default 12)
710732
*/
711733
public getColumn(): number {
712-
return this.opts.column;
734+
return this.opts.column as number;
713735
}
714736

715737
/** returns an array of grid HTML elements (no placeholder) - used to iterate through our children in DOM order */
@@ -783,7 +805,7 @@ export class GridStack {
783805
let relativeLeft = position.left - containerPos.left;
784806
let relativeTop = position.top - containerPos.top;
785807

786-
let columnWidth = (box.width / this.opts.column);
808+
let columnWidth = (box.width / this.getColumn());
787809
let rowHeight = (box.height / parseInt(this.el.getAttribute('gs-current-row')));
788810

789811
return {x: Math.floor(relativeLeft / columnWidth), y: Math.floor(relativeTop / rowHeight)};
@@ -1336,29 +1358,38 @@ export class GridStack {
13361358

13371359
/**
13381360
* called when we are being resized by the window - check if the one Column Mode needs to be turned on/off
1339-
* and remember the prev columns we used, as well as check for auto cell height (square)
1361+
* and remember the prev columns we used, or get our count from parent, as well as check for auto cell height (square)
13401362
*/
13411363
public onParentResize(): GridStack {
13421364
if (!this.el || !this.el.clientWidth) return; // return if we're gone or no size yet (will get called again)
1343-
let oneColumn = !this.opts.disableOneColumnMode && this.el.clientWidth <= this.opts.minWidth;
1344-
let changedOneColumn = false;
1365+
let changedColumn = false;
13451366

1346-
if ((this.opts.column === 1) !== oneColumn) {
1347-
changedOneColumn = true;
1348-
if (this.opts.animate) { this.setAnimation(false); } // 1 <-> 12 is too radical, turn off animation
1349-
this.column(oneColumn ? 1 : this._prevColumn);
1350-
if (this.opts.animate) { this.setAnimation(true); }
1367+
// see if we're nested and take our column count from our parent....
1368+
if (this._autoColumn && this.opts._isNested) {
1369+
if (this.opts.column !== this.opts._isNested.w) {
1370+
changedColumn = true;
1371+
this.column(this.opts._isNested.w, 'none');
1372+
}
1373+
} else {
1374+
// else check for 1 column in/out behavior
1375+
let oneColumn = !this.opts.disableOneColumnMode && this.el.clientWidth <= this.opts.minWidth;
1376+
if ((this.opts.column === 1) !== oneColumn) {
1377+
changedColumn = true;
1378+
if (this.opts.animate) { this.setAnimation(false); } // 1 <-> 12 is too radical, turn off animation
1379+
this.column(oneColumn ? 1 : this._prevColumn);
1380+
if (this.opts.animate) { this.setAnimation(true); }
1381+
}
13511382
}
13521383

13531384
// make the cells content square again
13541385
if (this._isAutoCellHeight) {
1355-
if (!changedOneColumn && this.opts.cellHeightThrottle) {
1386+
if (!changedColumn && this.opts.cellHeightThrottle) {
13561387
if (!this._cellHeightThrottle) {
13571388
this._cellHeightThrottle = Utils.throttle(() => this.cellHeight(), this.opts.cellHeightThrottle);
13581389
}
13591390
this._cellHeightThrottle();
13601391
} else {
1361-
// immediate update if we've changed to/from oneColumn or have no threshold
1392+
// immediate update if we've changed column count or have no threshold
13621393
this.cellHeight();
13631394
}
13641395
}

src/types.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -71,8 +71,11 @@ export interface GridStackOptions {
7171
/** list of children item to create when calling load() or addGrid() */
7272
children?: GridStackWidget[];
7373

74-
/** number of columns (default?: 12). Note: IF you change this, CSS also have to change. See https://github.com/gridstack/gridstack.js#change-grid-columns */
75-
column?: number;
74+
/** number of columns (default?: 12). Note: IF you change this, CSS also have to change. See https://github.com/gridstack/gridstack.js#change-grid-columns.
75+
* Note: for nested grids, it is recommended to use 'auto' which will always match the container grid-item current width (in column) to keep inside and outside
76+
* items always to same. flag is ignored for non nested grids.
77+
*/
78+
column?: number | 'auto';
7679

7780
/** additional class on top of '.grid-stack' (which is required for our CSS) to differentiate this instance.
7881
Note: only used by addGrid(), else your element should have the needed class */

0 commit comments

Comments
 (0)