Skip to content

Commit 4e5b095

Browse files
authored
Merge pull request #2050 from adumesny/master
using drag and drop to create nested Grids - part1
2 parents a1b1bf7 + c2e0dbb commit 4e5b095

14 files changed

+422
-140
lines changed

demo/demo.css

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,3 +60,16 @@ h1 {
6060
.sidebar .grid-stack-item .grid-stack-item-content {
6161
background: none;
6262
}
63+
64+
/* make nested grid have slightly darker bg take almost all space (need some to tell them apart) so items inside can have similar to external size+margin */
65+
.grid-stack > .grid-stack-item.grid-stack-sub-grid > .grid-stack-item-content {
66+
background: rgba(0,0,0,0.1);
67+
inset: 0 2px;
68+
}
69+
.grid-stack.grid-stack-nested {
70+
background: none;
71+
/* background-color: red; */
72+
/* take entire space */
73+
position: absolute;
74+
inset: 0; /* TODO change top: if you have content in nested grid */
75+
}

demo/float.html

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -23,20 +23,21 @@ <h1>Float grid demo</h1>
2323
<script src="events.js"></script>
2424
<script type="text/javascript">
2525
let grid = GridStack.init({
26-
float: true,
26+
// float: false,
2727
// disableResize: true, // TEST no resizing, but dragging
28-
resizable: { handles: 'all'} // do all sides for testing
28+
// resizable: { handles: 'all'} // do all sides for testing
29+
// draggable: { pause: true },
30+
subGrid: { createDynamic: true, column: 'auto' },
2931
});
3032
addEvents(grid);
3133

34+
let count = 0;
3235
let items = [
33-
{x: 1, y: 1}, //, locked:true, content:"locked"},
34-
{x: 2, y: 2, w: 3},
35-
{x: 4, y: 2},
36-
{x: 3, y: 1, h: 2},
37-
{x: 0, y: 6, w: 2, h: 2}
36+
{x: 0, y: 0},
37+
{x: 1, y: 0},
38+
{x: 2, y: 0, w: 2},
3839
];
39-
let count = 0;
40+
items.forEach(e => e.content = String(count++));
4041

4142
addNewWidget = function() {
4243
let n = items[count] || {
@@ -45,16 +46,15 @@ <h1>Float grid demo</h1>
4546
w: Math.round(1 + 3 * Math.random()),
4647
h: Math.round(1 + 3 * Math.random())
4748
};
48-
n.content = n.content || String(count);
49+
n.content = n.content || String(count++);
4950
grid.addWidget(n);
50-
count++;
5151
};
5252

5353
toggleFloat = function() {
5454
grid.float(! grid.getFloat());
5555
document.querySelector('#float').innerHTML = 'float: ' + grid.getFloat();
5656
};
57-
addNewWidget();
57+
grid.load(items);
5858
</script>
5959
</body>
6060
</html>

demo/index.html

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ <h1>Demos</h1>
1616
<li><a href="knockout.html">Knockout.js</a></li>
1717
<li><a href="mobile.html">Mobile touch (JQ)</a></li>
1818
<li><a href="nested.html">Nested grids</a></li>
19+
<li><a href="nested_constraint.html">Nested Constraint grids</a></li>
1920
<li><a href="nested_advanced.html">Nested Advanced grids</a></li>
2021
<li><a href="react-hooks.html">ReactJS (Hooks)</a></li>
2122
<li><a href="react.html">ReactJS</a></li>

demo/nested.html

Lines changed: 1 addition & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -4,29 +4,10 @@
44
<meta charset="utf-8">
55
<meta http-equiv="X-UA-Compatible" content="IE=edge">
66
<meta name="viewport" content="width=device-width, initial-scale=1">
7-
<title>Nested grids demo (ES6)</title>
7+
<title>Nested grids demo</title>
88
<link rel="stylesheet" href="demo.css"/>
99
<link rel="stylesheet" href="../dist/gridstack-extra.min.css"/>
1010
<script src="../dist/gridstack-all.js"></script>
11-
<style type="text/css">
12-
/* make nested grids have slightly darker bg */
13-
.grid-stack.grid-stack-nested {
14-
background: #e4e4c1;
15-
}
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: 0 2px; not IE */
19-
top: 0;
20-
bottom: 0;
21-
left: 2px;
22-
right: 2px;
23-
}
24-
/* make nested grid take entire item content */
25-
.grid-stack-item-content .grid-stack {
26-
min-height: 100%;
27-
min-width: 100%;
28-
}
29-
</style>
3011
</head>
3112
<body>
3213
<div class="container-fluid">

demo/nested_advanced.html

Lines changed: 15 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -4,26 +4,19 @@
44
<meta charset="utf-8">
55
<meta http-equiv="X-UA-Compatible" content="IE=edge">
66
<meta name="viewport" content="width=device-width, initial-scale=1">
7-
<title>Nested grids demo (ES6)</title>
7+
<title>Advance Nested grids demo</title>
88
<link rel="stylesheet" href="demo.css"/>
99
<link rel="stylesheet" href="../dist/gridstack-extra.min.css"/>
1010
<script src="../dist/gridstack-all.js"></script>
11-
<style type="text/css">
12-
.grid-stack.grid-stack-nested {
13-
background: rgba(255, 255, 255, 0.3);
14-
}
15-
.grid-stack-item.sub .grid-stack-item-content {
16-
background: lightpink;
17-
}
18-
</style>
1911
</head>
2012
<body>
2113
<div class="container-fluid">
2214
<h1>Advanced Nested grids demo</h1>
23-
<p>This example shows sub-grids only accepting pink items, while parent accept all.</p>
15+
<p>Create sub-grids on the fly, by dragging items completely over others (nest) vs partially (push) using
16+
the new v7 API <code>GridStackOptions.subGrid.createDynamic=true</code></p>
17+
<p>This will use the new delay drag&drop option <code>DDDragOpt.pause</code> to tell the gesture difference</p>
2418
<a class="btn btn-primary" onClick="addNested()" href="#">Add Widget</a>
25-
<a class="btn btn-primary" onClick="addNewWidget('.sub1')" href="#">Add Widget Grid1</a>
26-
<a class="btn btn-primary" onClick="addNewWidget('.sub2')" href="#">Add Widget Grid2</a>
19+
<a class="btn btn-primary" onClick="addNewWidget('sub1_grid')" href="#">Add Widget Grid1</a>
2720
<span>entire save/re-create:</span>
2821
<a class="btn btn-primary" onClick="save()" href="#">Save</a>
2922
<a class="btn btn-primary" onClick="destroy()" href="#">Destroy</a>
@@ -38,28 +31,27 @@ <h1>Advanced Nested grids demo</h1>
3831
</div>
3932

4033
<script type="text/javascript">
41-
let sub1 = [ {x:0, y:0}, {x:1, y:0}, {x:2, y:0}, {x:3, y:0}, {x:0, y:1}, {x:1, y:1}];
42-
let sub2 = [ {x:0, y:0}, {x:0, y:1, w:2}];
34+
let main = [{x:0, y:0}, {x:1, y:0}, {x:0, y:1}]
35+
let sub1 = [{x:0, y:0}, {x:1, y:0}];
4336
let count = 0;
44-
[...sub1, ...sub2].forEach(d => d.content = String(count++));
37+
[...main, ...sub1].forEach(d => d.content = String(count++));
4538
let subOptions = {
46-
cellHeight: 50,
39+
cellHeight: 50, // should be 50 - top/bottom
4740
column: 'auto', // size to match container. make sure to include gridstack-extra.min.css
48-
itemClass: 'sub', // style sub items differently and use to prevent dragging in/out
49-
acceptWidgets: '.grid-stack-item.sub', // only pink sub items can be inserted
50-
margin: 2,
51-
minRow: 1, // don't collapse when empty
41+
acceptWidgets: true, // will accept .grid-stack-item by default
42+
createDynamic: true, // NEW v7 api to create sub-grids on the fly
43+
margin: 5,
5244
};
5345
let options = { // main grid options
5446
cellHeight: 50,
5547
margin: 5,
5648
minRow: 2, // don't collapse when empty
5749
acceptWidgets: true,
5850
id: 'main',
51+
subGrid: subOptions,
5952
children: [
60-
{y:0, content: 'regular item'},
61-
{x:1, w:4, h:4, content: 'nested 1 - can drag items out', subGrid: {children: sub1, dragOut: true, class: 'sub1', ...subOptions}},
62-
{x:5, w:4, h:4, content: 'nested 2 - constrained to parent (JQ only)', subGrid: {children: sub2, class: 'sub2', ...subOptions}},
53+
...main,
54+
{x:2, y:0, w:2, h:3, subGrid: {children: sub1, id:'sub1_grid', ...subOptions}/*,content: "<div>nested grid here</div>"*/},
6355
]
6456
};
6557

demo/nested_constraint.html

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="utf-8">
5+
<meta http-equiv="X-UA-Compatible" content="IE=edge">
6+
<meta name="viewport" content="width=device-width, initial-scale=1">
7+
<title>Constraint nested grids demo</title>
8+
<link rel="stylesheet" href="demo.css"/>
9+
<link rel="stylesheet" href="../dist/gridstack-extra.min.css"/>
10+
<script src="../dist/gridstack-all.js"></script>
11+
<style type="text/css">
12+
.grid-stack-item.sub .grid-stack-item-content {
13+
background: lightpink;
14+
}
15+
</style>
16+
</head>
17+
<body>
18+
<div class="container-fluid">
19+
<h1>Constraint Nested grids demo</h1>
20+
<p>This example shows sub-grids only accepting pink items, while parent accept all.</p>
21+
<a class="btn btn-primary" onClick="addNested()" href="#">Add Widget</a>
22+
<a class="btn btn-primary" onClick="addNewWidget('.sub1')" href="#">Add Widget Grid1</a>
23+
<a class="btn btn-primary" onClick="addNewWidget('.sub2')" href="#">Add Widget Grid2</a>
24+
<span>entire save/re-create:</span>
25+
<a class="btn btn-primary" onClick="save()" href="#">Save</a>
26+
<a class="btn btn-primary" onClick="destroy()" href="#">Destroy</a>
27+
<a class="btn btn-primary" onClick="load()" href="#">Create</a>
28+
<span>partial save/load:</span>
29+
<a class="btn btn-primary" onClick="save(true, false)" href="#">Save list</a>
30+
<a class="btn btn-primary" onClick="save(false, false)" href="#">Save no content</a>
31+
<a class="btn btn-primary" onClick="destroy(false)" href="#">Clear</a>
32+
<a class="btn btn-primary" onClick="load(false)" href="#">Load</a>
33+
<br><br>
34+
<!-- grid will be added here -->
35+
</div>
36+
37+
<script type="text/javascript">
38+
let sub1 = [ {x:0, y:0}, {x:1, y:0}, {x:2, y:0}, {x:3, y:0}, {x:0, y:1}, {x:1, y:1}];
39+
let sub2 = [ {x:0, y:0}, {x:0, y:1, w:2}];
40+
let count = 0;
41+
[...sub1, ...sub2].forEach(d => d.content = String(count++));
42+
let subOptions = {
43+
cellHeight: 50,
44+
column: 'auto', // size to match container. make sure to include gridstack-extra.min.css
45+
itemClass: 'sub', // style sub items differently and use to prevent dragging in/out
46+
acceptWidgets: '.grid-stack-item.sub', // only pink sub items can be inserted
47+
margin: 2,
48+
minRow: 1, // don't collapse when empty
49+
};
50+
let options = { // main grid options
51+
cellHeight: 50,
52+
margin: 5,
53+
minRow: 2, // don't collapse when empty
54+
acceptWidgets: true,
55+
id: 'main',
56+
children: [
57+
{y:0, content: 'regular item'},
58+
{x:1, w:4, h:4, subGrid: {children: sub1, class: 'sub1', ...subOptions}},
59+
{x:5, w:4, h:4, subGrid: {children: sub2, class: 'sub2', ...subOptions}},
60+
]
61+
};
62+
63+
// create and load it all from JSON above
64+
let grid = GridStack.addGrid(document.querySelector('.container-fluid'), options);
65+
66+
addNested = function() {
67+
grid.addWidget({x:0, y:100, content:"new item"});
68+
}
69+
70+
addNewWidget = function(selector) {
71+
let subGrid = document.querySelector(selector).gridstack;
72+
let node = {
73+
x: Math.round(6 * Math.random()),
74+
y: Math.round(5 * Math.random()),
75+
w: Math.round(1 + 1 * Math.random()),
76+
h: Math.round(1 + 1 * Math.random()),
77+
content: String(count++)
78+
};
79+
subGrid.addWidget(node);
80+
return false;
81+
};
82+
83+
save = function(content = true, full = true) {
84+
options = grid.save(content, full);
85+
console.log(options);
86+
// console.log(JSON.stringify(options));
87+
}
88+
destroy = function(full = true) {
89+
if (full) {
90+
grid.destroy();
91+
grid = undefined;
92+
} else {
93+
grid.removeAll();
94+
}
95+
}
96+
load = function(full = true) {
97+
if (full) {
98+
grid = GridStack.addGrid(document.querySelector('.container-fluid'), options);
99+
} else {
100+
grid.load(options);
101+
}
102+
}
103+
104+
</script>
105+
</body>
106+
</html>

doc/CHANGES.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ Change log
55
<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->
66
**Table of Contents** *generated with [DocToc](http://doctoc.herokuapp.com/)*
77

8+
- [7-dev (TBD)](#7-dev-tbd)
89
- [6.0.2 (2022-09-23)](#602-2022-09-23)
910
- [6.0.1 (2022-08-27)](#601-2022-08-27)
1011
- [6.0.0 (2022-08-21)](#600-2022-08-21)
@@ -71,6 +72,13 @@ Change log
7172

7273
<!-- END doctoc generated TOC please keep comment here to allow auto update -->
7374

75+
## 7-dev (TBD)
76+
* add [#1009](https://github.com/gridstack/gridstack.js/issues/1009) Create sub-grids on the fly,
77+
by dragging items completely over others (nest) vs partially (push) using new flag `GridStackOptions.subGrid.createDynamic=true`.
78+
Thank you [StephanP] for sponsoring it.<br>
79+
See [advance Nested](https://github.com/gridstack/gridstack.js/blob/master/demo/nested_advanced.html)
80+
* add - ability to pause drag&drop collision until the user stops moving - see `DDDragOpt.pause` (used for creating nested grids on the fly based on gesture).
81+
7482
## 6.0.2 (2022-09-23)
7583
* fixed [#2034](https://github.com/gridstack/gridstack.js/issues/2034) `removeWidget()` breaking resize handle feedback
7684
* fixed [#2043](https://github.com/gridstack/gridstack.js/issues/2043) when swapping shapes in maxRow grid, make sure we still check for 50% coverage

src/dd-draggable.ts

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,8 @@ export class DDDraggable extends DDBaseImplement implements HTMLElementExtendOpt
5757
protected helperContainment: HTMLElement;
5858
/** @internal properties we change during dragging, and restore back */
5959
protected static originStyleProp = ['transition', 'pointerEvents', 'position', 'left', 'top'];
60+
/** @internal pause before we call the actual drag hit collision code */
61+
protected dragTimeout: number;
6062

6163
constructor(el: HTMLElement, option: DDDraggableOpt = {}) {
6264
super();
@@ -106,6 +108,8 @@ export class DDDraggable extends DDBaseImplement implements HTMLElementExtendOpt
106108
}
107109

108110
public destroy(): void {
111+
if (this.dragTimeout) window.clearTimeout(this.dragTimeout);
112+
delete this.dragTimeout;
109113
if (this.dragging) this._mouseUp(this.mouseDownEvent);
110114
this.disable(true);
111115
delete this.el;
@@ -148,18 +152,31 @@ export class DDDraggable extends DDBaseImplement implements HTMLElementExtendOpt
148152
return true;
149153
}
150154

155+
/** @internal method to call actual drag event */
156+
protected _callDrag(e: DragEvent) {
157+
if (!this.dragging) return;
158+
const ev = Utils.initEvent<DragEvent>(e, { target: this.el, type: 'drag' });
159+
if (this.option.drag) {
160+
this.option.drag(ev, this.ui());
161+
}
162+
this.triggerEvent('drag', ev);
163+
}
164+
151165
/** @internal called when the main page (after successful mousedown) receives a move event to drag the item around the screen */
152166
protected _mouseMove(e: DragEvent): boolean {
153167
// console.log(`${count++} move ${e.x},${e.y}`)
154168
let s = this.mouseDownEvent;
155169

156170
if (this.dragging) {
157171
this._dragFollow(e);
158-
const ev = Utils.initEvent<DragEvent>(e, { target: this.el, type: 'drag' });
159-
if (this.option.drag) {
160-
this.option.drag(ev, this.ui());
172+
// delay actual grid handling drag until we pause for a while if set
173+
if (DDManager.pauseDrag) {
174+
const pause = Number.isInteger(DDManager.pauseDrag) ? DDManager.pauseDrag as number : 100;
175+
if (this.dragTimeout) window.clearTimeout(this.dragTimeout);
176+
this.dragTimeout = window.setTimeout(() => this._callDrag(e), pause);
177+
} else {
178+
this._callDrag(e);
161179
}
162-
this.triggerEvent('drag', ev);
163180
} else if (Math.abs(e.x - s.x) + Math.abs(e.y - s.y) > 3) {
164181
/**
165182
* don't start unless we've moved at least 3 pixels

0 commit comments

Comments
 (0)