Skip to content

Commit 2612ee2

Browse files
committed
new addGrid() method to create a grid + children
* added new API to create a grid and it's children * load() will now recursively create nested grid * updated nested.html to use new API * fixed tests TODO: next have save() work on nested grids (API will have to change)
1 parent eab65c0 commit 2612ee2

File tree

6 files changed

+77
-48
lines changed

6 files changed

+77
-48
lines changed

demo/nested.html

Lines changed: 15 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -25,47 +25,29 @@ <h1>Nested grids demo</h1>
2525
<a class="btn btn-primary" onClick="addNewWidget(grid2)" href="#">Add Widget Grid2</a>
2626
<br><br>
2727

28-
<div class="grid-stack top">
29-
<div class="grid-stack-item" gs-x="0" gs-y="0" gs-w="1">
30-
<div class="grid-stack-item-content">regular item</div>
31-
</div>
32-
<div class="grid-stack-item" gs-x="1" gs-y="0" gs-w="4" gs-h="4">
33-
<div class="grid-stack-item-content">
34-
nested 1 - can drag items out
35-
<div class="grid-stack nested1">
36-
<div class="grid-stack-item sub" gs-x="0" gs-y="0" gs-w="3"><div class="grid-stack-item-content">1</div></div>
37-
<div class="grid-stack-item sub" gs-x="3" gs-y="0" gs-w="3"><div class="grid-stack-item-content">2</div></div>
38-
<div class="grid-stack-item sub" gs-x="6" gs-y="0" gs-w="3"><div class="grid-stack-item-content">3</div></div>
39-
<div class="grid-stack-item sub" gs-x="9" gs-y="0" gs-w="3"><div class="grid-stack-item-content">4</div></div>
40-
<div class="grid-stack-item sub" gs-x="0" gs-y="1" gs-w="3"><div class="grid-stack-item-content">5</div></div>
41-
<div class="grid-stack-item sub" gs-x="3" gs-y="1" gs-w="3"><div class="grid-stack-item-content">6</div></div>
42-
</div>
43-
</div>
44-
</div>
45-
<div class="grid-stack-item" gs-x="5" gs-y="0" gs-w="3" gs-h="4">
46-
<div class="grid-stack-item-content">
47-
nested 2 - constrained to parent (default)
48-
<div class="grid-stack nested2">
49-
<div class="grid-stack-item sub" gs-x="0" gs-y="0" gs-w="3"><div class="grid-stack-item-content">7</div></div>
50-
<div class="grid-stack-item sub" gs-x="0" gs-y="3" gs-w="3"><div class="grid-stack-item-content">8</div></div>
51-
</div>
52-
</div>
53-
</div>
28+
<div class="grid-stack"></div>
5429
</div>
5530

5631
<script type="text/javascript">
57-
let nestOptions = {
32+
let sub1 = [ {w: 3}, {w: 3}, {w: 3}, {w: 3}, {w: 3}, {w: 3}];
33+
let sub2 = [ {w: 3}, {x:0, y: 1, w: 3}];
34+
let count = 0;
35+
[...sub1, ...sub2].forEach(d => d.content = String(count++));
36+
let subOptions = {
5837
acceptWidgets: '.grid-stack-item.sub', // only pink sub items can be inserted, otherwise grid-items causes all sort of issues
59-
dragOut: true, // let us drag them out!
6038
disableOneColumnMode: true, // nested are small, but still want N columns
39+
itemClass: 'sub',
6140
margin: 1
6241
};
63-
let grid0 = GridStack.init({cellHeight: 70}, '.grid-stack.top');
64-
let grid1 = GridStack.init(nestOptions, '.grid-stack.nested1');
65-
nestOptions.dragOut = false;
66-
let grid2 = GridStack.init(nestOptions, '.grid-stack.nested2');
42+
let items = [
43+
{w:1, content: 'regular item'},
44+
{x:1, w:4, h:4, content: 'nested 1 - can drag items out', subGrid: {children: sub1, dragOut: true, class: 'nested1', ...subOptions}},
45+
{x:5, w:4, h:4, content: 'nested 2 - constrained to parent (default)', subGrid: {children: sub2, class: 'nested2', ...subOptions}},
46+
];
47+
48+
let grid = GridStack.init({cellHeight: 70});
49+
grid.load(items);
6750

68-
let count = 9;
6951
addNewWidget = function(grid) {
7052
let node = {
7153
x: Math.round(12 * Math.random()),

doc/CHANGES.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,8 @@ Change log
4444

4545
## 3.3.0-dev
4646

47-
- TBD
47+
- add new `addGrid(parent, opts)` to create a grid and load children, which is used by `load()` to supports nested grids creation. see [nested.html](https://github.com/gridstack/gridstack.js/blob/develop/demo/nested.html) demo.
48+
4849
## 3.3.0 (2020-11-29)
4950

5051
- the big news is we finally have a native HTML5 drag&drop plugin (zero jquery)! Huge thanks to [@rhlin](https://github.com/rhlin) for creating this in stealth mode. Read all about it in main doc.

doc/README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,9 @@ gridstack.js API
7474
* a string (ex: '100px', '10em', '10rem', '10%', `10vh')
7575
* 0 or null, in which case the library will not generate styles for rows. Everything must be defined in CSS files.
7676
* `'auto'` - height will be square cells initially.
77+
- `children`?: GridStackWidget[] - list of children item to create when calling load() or addGrid()
7778
- `column` - number of columns (default: `12`) which can change on the fly with `column(N)` as well. See [example](http://gridstackjs.com/demo/column.html)
79+
- `class`?: string - additional class on top of '.grid-stack' (which is required for our CSS) to differentiate this instance
7880
- `disableDrag` - disallows dragging of widgets (default: `false`).
7981
- `disableOneColumnMode` - disables the onColumnMode when the grid width is less than minW (default: 'false')
8082
- `disableResize` - disallows resizing of widgets (default: `false`).
@@ -130,6 +132,7 @@ You need to add `noResize` and `noMove` attributes to completely lock the widget
130132
- `resizeHandles` - sets resize handles for a specific widget.
131133
- `id`- (number | string) good for quick identification (for example in change event)
132134
- `content` - (string) html content to be added when calling `grid.load()/addWidget()` as content inside the item
135+
- `subGrid`: GridStackOptions - optional nested grid options and list of children
133136

134137
## Item attributes
135138

spec/gridstack-spec.ts

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -607,10 +607,10 @@ describe('gridstack', function() {
607607
let items = Utils.getElements('.grid-stack-item');
608608
for (let i = 0; i < items.length; i++) {
609609
grid
610-
.minW(items[i], 2)
611-
.maxW(items[i], 3)
612-
.minH(items[i], 4)
613-
.maxH(items[i], 5);
610+
.minWidth(items[i], 2)
611+
.maxWidth(items[i], 3)
612+
.minHeight(items[i], 4)
613+
.maxHeight(items[i], 5);
614614
}
615615
for (let j = 0; j < items.length; j++) {
616616
expect(parseInt(items[j].getAttribute('gs-min-w'), 10)).toBe(2);
@@ -620,10 +620,10 @@ describe('gridstack', function() {
620620
}
621621
// remove all constrain
622622
grid
623-
.minW('grid-stack-item', 0)
624-
.maxW('.grid-stack-item', null)
625-
.minH('grid-stack-item', undefined)
626-
.maxH(undefined, 0);
623+
.minWidth('grid-stack-item', 0)
624+
.maxWidth('.grid-stack-item', null)
625+
.minHeight('grid-stack-item', undefined)
626+
.maxHeight(undefined, 0);
627627
for (let j = 0; j < items.length; j++) {
628628
expect(items[j].getAttribute('gs-min-w')).toBe(null);
629629
expect(items[j].getAttribute('gs-max-w')).toBe(null);
@@ -1753,7 +1753,7 @@ describe('gridstack', function() {
17531753
});
17541754
it('load size 1 item only with callback', function() {
17551755
let grid = GridStack.init();
1756-
grid.load([{h:3, id:'gsItem1'}], () => {});
1756+
grid.load([{h:3, id:'gsItem1'}], () => null);
17571757
let layout = grid.save(false);
17581758
expect(layout).toEqual([{x:0, y:0, w:4, h:3, id:'gsItem1'}, {x:4, y:0, w:4, h:4, id:'gsItem2'}]);
17591759
});

src/gridstack.ts

Lines changed: 40 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -329,7 +329,7 @@ export class GridStack {
329329
let content = els ? (els as GridStackWidget).content || '' : '';
330330
options = els;
331331
let doc = document.implementation.createHTMLDocument();
332-
doc.body.innerHTML = `<div class="grid-stack-item"><div class="grid-stack-item-content">${content}</div></div>`;
332+
doc.body.innerHTML = `<div class="grid-stack-item ${this.opts.itemClass || ''}"><div class="grid-stack-item-content">${content}</div></div>`;
333333
el = doc.body.children[0] as HTMLElement;
334334
} else {
335335
el = els as HTMLElement;
@@ -372,6 +372,28 @@ export class GridStack {
372372
return list;
373373
}
374374

375+
/**
376+
* call to create a grid with the given options, including loading any children
377+
* @param parent HTML element parent to the grid
378+
* @param opt grids options used to initialize the grid, and list of children
379+
*/
380+
public addGrid(parent: HTMLElement, opt: GridStackOptions = {}): GridStack {
381+
if (!parent) { return null; }
382+
383+
// create the grid element
384+
let doc = document.implementation.createHTMLDocument();
385+
doc.body.innerHTML = `<div class="grid-stack ${opt.class || ''}"></div>`;
386+
let el = doc.body.children[0] as HTMLElement;
387+
parent.append(el);
388+
389+
// create grid class and load any children
390+
let grid = GridStack.init(opt, el);
391+
if (opt.children) {
392+
grid.load(opt.children);
393+
}
394+
return grid;
395+
}
396+
375397
/**
376398
* load the widgets from a list. This will call update() on each (matching by id) or add/remove widgets that are not there.
377399
*
@@ -382,7 +404,7 @@ export class GridStack {
382404
* @example
383405
* see http://gridstackjs.com/demo/serialization.html
384406
**/
385-
public load(layout: GridStackWidget[], addAndRemove: boolean | ((w: GridStackWidget, add: boolean) => void) = true): GridStack {
407+
public load(layout: GridStackWidget[], addAndRemove: boolean | ((g: GridStack, w: GridStackWidget, add: boolean) => GridItemHTMLElement) = true): GridStack {
386408
let items = GridStack.Utils.sort(layout, -1, this._prevColumn || this.opts.column);
387409

388410
// 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
@@ -394,34 +416,47 @@ export class GridStack {
394416

395417
let removed: GridStackNode[] = [];
396418
this.batchUpdate();
419+
397420
// see if any items are missing from new layout and need to be removed first
398421
if (addAndRemove) {
399422
let copyNodes = [...this.engine.nodes]; // don't loop through array you modify
400423
copyNodes.forEach(n => {
401424
let item = items.find(w => n.id === w.id);
402425
if (!item) {
403426
if (typeof(addAndRemove) === 'function') {
404-
addAndRemove(n, false);
427+
addAndRemove(this, n, false);
405428
} else {
406429
removed.push(n); // batch keep track
407430
this.removeWidget(n.el, true, false);
408431
}
409432
}
410433
});
411434
}
435+
412436
// now add/update the widgets
413437
items.forEach(w => {
414438
let item = (w.id || w.id === 0) ? this.engine.nodes.find(n => n.id === w.id) : undefined;
415439
if (item) {
416440
this.update(item.el, w);
441+
if (w.subGrid && w.subGrid.children) { // update any sub grid as well
442+
let sub = item.el.querySelector('.grid-stack') as GridHTMLElement;
443+
if (sub && sub.gridstack) {
444+
sub.gridstack.load(w.subGrid.children); // TODO: support updating grid options ?
445+
}
446+
}
417447
} else if (addAndRemove) {
418448
if (typeof(addAndRemove) === 'function') {
419-
addAndRemove(w, true);
449+
w = addAndRemove(this, w, true).gridstackNode;
420450
} else {
421-
this.addWidget(w);
451+
w = this.addWidget(w).gridstackNode;
452+
}
453+
if (w.subGrid) { // see if there is a sub-grid to create too
454+
let content = w.el.querySelector('.grid-stack-item-content') as HTMLElement;
455+
this.addGrid(content, w.subGrid);
422456
}
423457
}
424458
});
459+
425460
this.engine.removedNodes = removed;
426461
this.commit();
427462

src/types.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,9 +59,15 @@ export interface GridStackOptions {
5959
/** (internal) unit for cellHeight (default? 'px') which is set when a string cellHeight with a unit is passed (ex: '10rem') */
6060
cellHeightUnit?: string;
6161

62+
/** list of children item to create when calling load() or addGrid() */
63+
children?: GridStackWidget[];
64+
6265
/** 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 */
6366
column?: number;
6467

68+
/** additional class on top of '.grid-stack' (which is required for our CSS) to differentiate this instance */
69+
class?: string;
70+
6571
/** disallows dragging of widgets (default?: false) */
6672
disableDrag?: boolean;
6773

@@ -212,6 +218,8 @@ export interface GridStackWidget {
212218
id?: numberOrString;
213219
/** html to append inside as content */
214220
content?: string;
221+
/** optional nested grid options and list of children */
222+
subGrid?: GridStackOptions;
215223
}
216224

217225
/** Drag&Drop resize options */

0 commit comments

Comments
 (0)