Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

using drag and drop to create nested Grids - part1 #2050

Merged
merged 1 commit into from Sep 24, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
13 changes: 13 additions & 0 deletions demo/demo.css
Expand Up @@ -60,3 +60,16 @@ h1 {
.sidebar .grid-stack-item .grid-stack-item-content {
background: none;
}

/* 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 */
.grid-stack > .grid-stack-item.grid-stack-sub-grid > .grid-stack-item-content {
background: rgba(0,0,0,0.1);
inset: 0 2px;
}
.grid-stack.grid-stack-nested {
background: none;
/* background-color: red; */
/* take entire space */
position: absolute;
inset: 0; /* TODO change top: if you have content in nested grid */
}
22 changes: 11 additions & 11 deletions demo/float.html
Expand Up @@ -23,20 +23,21 @@ <h1>Float grid demo</h1>
<script src="events.js"></script>
<script type="text/javascript">
let grid = GridStack.init({
float: true,
// float: false,
// disableResize: true, // TEST no resizing, but dragging
resizable: { handles: 'all'} // do all sides for testing
// resizable: { handles: 'all'} // do all sides for testing
// draggable: { pause: true },
subGrid: { createDynamic: true, column: 'auto' },
});
addEvents(grid);

let count = 0;
let items = [
{x: 1, y: 1}, //, locked:true, content:"locked"},
{x: 2, y: 2, w: 3},
{x: 4, y: 2},
{x: 3, y: 1, h: 2},
{x: 0, y: 6, w: 2, h: 2}
{x: 0, y: 0},
{x: 1, y: 0},
{x: 2, y: 0, w: 2},
];
let count = 0;
items.forEach(e => e.content = String(count++));

addNewWidget = function() {
let n = items[count] || {
Expand All @@ -45,16 +46,15 @@ <h1>Float grid demo</h1>
w: Math.round(1 + 3 * Math.random()),
h: Math.round(1 + 3 * Math.random())
};
n.content = n.content || String(count);
n.content = n.content || String(count++);
grid.addWidget(n);
count++;
};

toggleFloat = function() {
grid.float(! grid.getFloat());
document.querySelector('#float').innerHTML = 'float: ' + grid.getFloat();
};
addNewWidget();
grid.load(items);
</script>
</body>
</html>
1 change: 1 addition & 0 deletions demo/index.html
Expand Up @@ -16,6 +16,7 @@ <h1>Demos</h1>
<li><a href="knockout.html">Knockout.js</a></li>
<li><a href="mobile.html">Mobile touch (JQ)</a></li>
<li><a href="nested.html">Nested grids</a></li>
<li><a href="nested_constraint.html">Nested Constraint grids</a></li>
<li><a href="nested_advanced.html">Nested Advanced grids</a></li>
<li><a href="react-hooks.html">ReactJS (Hooks)</a></li>
<li><a href="react.html">ReactJS</a></li>
Expand Down
21 changes: 1 addition & 20 deletions demo/nested.html
Expand Up @@ -4,29 +4,10 @@
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Nested grids demo (ES6)</title>
<title>Nested grids demo</title>
<link rel="stylesheet" href="demo.css"/>
<link rel="stylesheet" href="../dist/gridstack-extra.min.css"/>
<script src="../dist/gridstack-all.js"></script>
<style type="text/css">
/* make nested grids have slightly darker bg */
.grid-stack.grid-stack-nested {
background: #e4e4c1;
}
/* make nested grid take almost all space (need some to tell them apart) so items inside can have similar to external size+margin */
.grid-stack > .grid-stack-item.grid-stack-nested > .grid-stack-item-content {
/* inset: 0 2px; not IE */
top: 0;
bottom: 0;
left: 2px;
right: 2px;
}
/* make nested grid take entire item content */
.grid-stack-item-content .grid-stack {
min-height: 100%;
min-width: 100%;
}
</style>
</head>
<body>
<div class="container-fluid">
Expand Down
38 changes: 15 additions & 23 deletions demo/nested_advanced.html
Expand Up @@ -4,26 +4,19 @@
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Nested grids demo (ES6)</title>
<title>Advance Nested grids demo</title>
<link rel="stylesheet" href="demo.css"/>
<link rel="stylesheet" href="../dist/gridstack-extra.min.css"/>
<script src="../dist/gridstack-all.js"></script>
<style type="text/css">
.grid-stack.grid-stack-nested {
background: rgba(255, 255, 255, 0.3);
}
.grid-stack-item.sub .grid-stack-item-content {
background: lightpink;
}
</style>
</head>
<body>
<div class="container-fluid">
<h1>Advanced Nested grids demo</h1>
<p>This example shows sub-grids only accepting pink items, while parent accept all.</p>
<p>Create sub-grids on the fly, by dragging items completely over others (nest) vs partially (push) using
the new v7 API <code>GridStackOptions.subGrid.createDynamic=true</code></p>
<p>This will use the new delay drag&drop option <code>DDDragOpt.pause</code> to tell the gesture difference</p>
<a class="btn btn-primary" onClick="addNested()" href="#">Add Widget</a>
<a class="btn btn-primary" onClick="addNewWidget('.sub1')" href="#">Add Widget Grid1</a>
<a class="btn btn-primary" onClick="addNewWidget('.sub2')" href="#">Add Widget Grid2</a>
<a class="btn btn-primary" onClick="addNewWidget('sub1_grid')" href="#">Add Widget Grid1</a>
<span>entire save/re-create:</span>
<a class="btn btn-primary" onClick="save()" href="#">Save</a>
<a class="btn btn-primary" onClick="destroy()" href="#">Destroy</a>
Expand All @@ -38,28 +31,27 @@ <h1>Advanced Nested grids demo</h1>
</div>

<script type="text/javascript">
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}];
let sub2 = [ {x:0, y:0}, {x:0, y:1, w:2}];
let main = [{x:0, y:0}, {x:1, y:0}, {x:0, y:1}]
let sub1 = [{x:0, y:0}, {x:1, y:0}];
let count = 0;
[...sub1, ...sub2].forEach(d => d.content = String(count++));
[...main, ...sub1].forEach(d => d.content = String(count++));
let subOptions = {
cellHeight: 50,
cellHeight: 50, // should be 50 - top/bottom
column: 'auto', // size to match container. make sure to include gridstack-extra.min.css
itemClass: 'sub', // style sub items differently and use to prevent dragging in/out
acceptWidgets: '.grid-stack-item.sub', // only pink sub items can be inserted
margin: 2,
minRow: 1, // don't collapse when empty
acceptWidgets: true, // will accept .grid-stack-item by default
createDynamic: true, // NEW v7 api to create sub-grids on the fly
margin: 5,
};
let options = { // main grid options
cellHeight: 50,
margin: 5,
minRow: 2, // don't collapse when empty
acceptWidgets: true,
id: 'main',
subGrid: subOptions,
children: [
{y:0, content: 'regular item'},
{x:1, w:4, h:4, content: 'nested 1 - can drag items out', subGrid: {children: sub1, dragOut: true, class: 'sub1', ...subOptions}},
{x:5, w:4, h:4, content: 'nested 2 - constrained to parent (JQ only)', subGrid: {children: sub2, class: 'sub2', ...subOptions}},
...main,
{x:2, y:0, w:2, h:3, subGrid: {children: sub1, id:'sub1_grid', ...subOptions}/*,content: "<div>nested grid here</div>"*/},
]
};

Expand Down
106 changes: 106 additions & 0 deletions demo/nested_constraint.html
@@ -0,0 +1,106 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Constraint nested grids demo</title>
<link rel="stylesheet" href="demo.css"/>
<link rel="stylesheet" href="../dist/gridstack-extra.min.css"/>
<script src="../dist/gridstack-all.js"></script>
<style type="text/css">
.grid-stack-item.sub .grid-stack-item-content {
background: lightpink;
}
</style>
</head>
<body>
<div class="container-fluid">
<h1>Constraint Nested grids demo</h1>
<p>This example shows sub-grids only accepting pink items, while parent accept all.</p>
<a class="btn btn-primary" onClick="addNested()" href="#">Add Widget</a>
<a class="btn btn-primary" onClick="addNewWidget('.sub1')" href="#">Add Widget Grid1</a>
<a class="btn btn-primary" onClick="addNewWidget('.sub2')" href="#">Add Widget Grid2</a>
<span>entire save/re-create:</span>
<a class="btn btn-primary" onClick="save()" href="#">Save</a>
<a class="btn btn-primary" onClick="destroy()" href="#">Destroy</a>
<a class="btn btn-primary" onClick="load()" href="#">Create</a>
<span>partial save/load:</span>
<a class="btn btn-primary" onClick="save(true, false)" href="#">Save list</a>
<a class="btn btn-primary" onClick="save(false, false)" href="#">Save no content</a>
<a class="btn btn-primary" onClick="destroy(false)" href="#">Clear</a>
<a class="btn btn-primary" onClick="load(false)" href="#">Load</a>
<br><br>
<!-- grid will be added here -->
</div>

<script type="text/javascript">
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}];
let sub2 = [ {x:0, y:0}, {x:0, y:1, w:2}];
let count = 0;
[...sub1, ...sub2].forEach(d => d.content = String(count++));
let subOptions = {
cellHeight: 50,
column: 'auto', // size to match container. make sure to include gridstack-extra.min.css
itemClass: 'sub', // style sub items differently and use to prevent dragging in/out
acceptWidgets: '.grid-stack-item.sub', // only pink sub items can be inserted
margin: 2,
minRow: 1, // don't collapse when empty
};
let options = { // main grid options
cellHeight: 50,
margin: 5,
minRow: 2, // don't collapse when empty
acceptWidgets: true,
id: 'main',
children: [
{y:0, content: 'regular item'},
{x:1, w:4, h:4, subGrid: {children: sub1, class: 'sub1', ...subOptions}},
{x:5, w:4, h:4, subGrid: {children: sub2, class: 'sub2', ...subOptions}},
]
};

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

addNested = function() {
grid.addWidget({x:0, y:100, content:"new item"});
}

addNewWidget = function(selector) {
let subGrid = document.querySelector(selector).gridstack;
let node = {
x: Math.round(6 * Math.random()),
y: Math.round(5 * Math.random()),
w: Math.round(1 + 1 * Math.random()),
h: Math.round(1 + 1 * Math.random()),
content: String(count++)
};
subGrid.addWidget(node);
return false;
};

save = function(content = true, full = true) {
options = grid.save(content, full);
console.log(options);
// console.log(JSON.stringify(options));
}
destroy = function(full = true) {
if (full) {
grid.destroy();
grid = undefined;
} else {
grid.removeAll();
}
}
load = function(full = true) {
if (full) {
grid = GridStack.addGrid(document.querySelector('.container-fluid'), options);
} else {
grid.load(options);
}
}

</script>
</body>
</html>
8 changes: 8 additions & 0 deletions doc/CHANGES.md
Expand Up @@ -5,6 +5,7 @@ Change log
<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->
**Table of Contents** *generated with [DocToc](http://doctoc.herokuapp.com/)*

- [7-dev (TBD)](#7-dev-tbd)
- [6.0.2 (2022-09-23)](#602-2022-09-23)
- [6.0.1 (2022-08-27)](#601-2022-08-27)
- [6.0.0 (2022-08-21)](#600-2022-08-21)
Expand Down Expand Up @@ -71,6 +72,13 @@ Change log

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

## 7-dev (TBD)
* add [#1009](https://github.com/gridstack/gridstack.js/issues/1009) Create sub-grids on the fly,
by dragging items completely over others (nest) vs partially (push) using new flag `GridStackOptions.subGrid.createDynamic=true`.
Thank you [StephanP] for sponsoring it.<br>
See [advance Nested](https://github.com/gridstack/gridstack.js/blob/master/demo/nested_advanced.html)
* 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).

## 6.0.2 (2022-09-23)
* fixed [#2034](https://github.com/gridstack/gridstack.js/issues/2034) `removeWidget()` breaking resize handle feedback
* fixed [#2043](https://github.com/gridstack/gridstack.js/issues/2043) when swapping shapes in maxRow grid, make sure we still check for 50% coverage
Expand Down
25 changes: 21 additions & 4 deletions src/dd-draggable.ts
Expand Up @@ -57,6 +57,8 @@ export class DDDraggable extends DDBaseImplement implements HTMLElementExtendOpt
protected helperContainment: HTMLElement;
/** @internal properties we change during dragging, and restore back */
protected static originStyleProp = ['transition', 'pointerEvents', 'position', 'left', 'top'];
/** @internal pause before we call the actual drag hit collision code */
protected dragTimeout: number;

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

public destroy(): void {
if (this.dragTimeout) window.clearTimeout(this.dragTimeout);
delete this.dragTimeout;
if (this.dragging) this._mouseUp(this.mouseDownEvent);
this.disable(true);
delete this.el;
Expand Down Expand Up @@ -148,18 +152,31 @@ export class DDDraggable extends DDBaseImplement implements HTMLElementExtendOpt
return true;
}

/** @internal method to call actual drag event */
protected _callDrag(e: DragEvent) {
if (!this.dragging) return;
const ev = Utils.initEvent<DragEvent>(e, { target: this.el, type: 'drag' });
if (this.option.drag) {
this.option.drag(ev, this.ui());
}
this.triggerEvent('drag', ev);
}

/** @internal called when the main page (after successful mousedown) receives a move event to drag the item around the screen */
protected _mouseMove(e: DragEvent): boolean {
// console.log(`${count++} move ${e.x},${e.y}`)
let s = this.mouseDownEvent;

if (this.dragging) {
this._dragFollow(e);
const ev = Utils.initEvent<DragEvent>(e, { target: this.el, type: 'drag' });
if (this.option.drag) {
this.option.drag(ev, this.ui());
// delay actual grid handling drag until we pause for a while if set
if (DDManager.pauseDrag) {
const pause = Number.isInteger(DDManager.pauseDrag) ? DDManager.pauseDrag as number : 100;
if (this.dragTimeout) window.clearTimeout(this.dragTimeout);
this.dragTimeout = window.setTimeout(() => this._callDrag(e), pause);
} else {
this._callDrag(e);
}
this.triggerEvent('drag', ev);
} else if (Math.abs(e.x - s.x) + Math.abs(e.y - s.y) > 3) {
/**
* don't start unless we've moved at least 3 pixels
Expand Down