Skip to content

subgrid now propagate events to topmost grid #3031

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

Merged
merged 1 commit into from
Apr 24, 2025
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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 14 additions & 14 deletions demo/events.js
Original file line number Diff line number Diff line change
@@ -1,59 +1,59 @@
function addEvents(grid, id) {
let g = (id !== undefined ? 'grid' + id + ' ' : '');
let g = (id !== undefined ? 'grid' + id : '');

grid.on('added removed change', function(event, items) {
let str = '';
items.forEach(function(item) { str += ' (' + item.x + ',' + item.y + ' ' + item.w + 'x' + item.h + ')'; });
console.log(g + event.type + ' ' + items.length + ' items (x,y w h):' + str );
console.log((g || items[0].grid.opts.id) + ' ' + event.type + ' ' + items.length + ' items (x,y w h):' + str );
})
.on('enable', function(event) {
let grid = event.target;
console.log(g + 'enable');
let el = event.target;
console.log((g || el.gridstackNode.grid.opts.id) + ' enable');
})
.on('disable', function(event) {
let grid = event.target;
console.log(g + 'disable');
let el = event.target;
console.log((g || el.gridstackNode.grid.opts.id) + ' disable');
})
.on('dragstart', function(event, el) {
let n = el.gridstackNode;
let x = el.getAttribute('gs-x'); // verify node (easiest) and attr are the same
let y = el.getAttribute('gs-y');
console.log(g + 'dragstart ' + (n.content || '') + ' pos: (' + n.x + ',' + n.y + ') = (' + x + ',' + y + ')');
console.log((g || el.gridstackNode.grid.opts.id) + ' dragstart ' + (n.content || '') + ' pos: (' + n.x + ',' + n.y + ') = (' + x + ',' + y + ')');
})
.on('drag', function(event, el) {
let n = el.gridstackNode;
let x = el.getAttribute('gs-x'); // verify node (easiest) and attr are the same
let y = el.getAttribute('gs-y');
// console.log(g + 'drag ' + (n.content || '') + ' pos: (' + n.x + ',' + n.y + ') = (' + x + ',' + y + ')');
// console.log((g || el.gridstackNode.grid.opts.id) + ' drag ' + (n.content || '') + ' pos: (' + n.x + ',' + n.y + ') = (' + x + ',' + y + ')');
})
.on('dragstop', function(event, el) {
let n = el.gridstackNode;
let x = el.getAttribute('gs-x'); // verify node (easiest) and attr are the same
let y = el.getAttribute('gs-y');
console.log(g + 'dragstop ' + (n.content || '') + ' pos: (' + n.x + ',' + n.y + ') = (' + x + ',' + y + ')');
console.log((g || el.gridstackNode.grid.opts.id) + ' dragstop ' + (n.content || '') + ' pos: (' + n.x + ',' + n.y + ') = (' + x + ',' + y + ')');
})
.on('dropped', function(event, previousNode, newNode) {
if (previousNode) {
console.log(g + 'dropped - Removed widget from grid:', previousNode);
console.log((g || previousNode.grid.opts.id) + ' dropped - Removed widget from grid:', previousNode);
}
if (newNode) {
console.log(g + 'dropped - Added widget in grid:', newNode);
console.log((g || newNode.grid.opts.id) + ' dropped - Added widget in grid:', newNode);
}
})
.on('resizestart', function(event, el) {
let n = el.gridstackNode;
let rec = el.getBoundingClientRect();
console.log(`${g} resizestart ${n.content || ''} size: (${n.w}x${n.h}) = (${Math.round(rec.width)}x${Math.round(rec.height)})px`);
console.log(`${g || el.gridstackNode.grid.opts.id} resizestart ${n.content || ''} size: (${n.w}x${n.h}) = (${Math.round(rec.width)}x${Math.round(rec.height)})px`);

})
.on('resize', function(event, el) {
let n = el.gridstackNode;
let rec = el.getBoundingClientRect();
console.log(`${g} resize ${n.content || ''} size: (${n.w}x${n.h}) = (${Math.round(rec.width)}x${Math.round(rec.height)})px`);
console.log(`${g || el.gridstackNode.grid.opts.id} resize ${n.content || ''} size: (${n.w}x${n.h}) = (${Math.round(rec.width)}x${Math.round(rec.height)})px`);
})
.on('resizestop', function(event, el) {
let n = el.gridstackNode;
let rec = el.getBoundingClientRect();
console.log(`${g} resizestop ${n.content || ''} size: (${n.w}x${n.h}) = (${Math.round(rec.width)}x${Math.round(rec.height)})px`);
console.log(`${g || el.gridstackNode.grid.opts.id} resizestop ${n.content || ''} size: (${n.w}x${n.h}) = (${Math.round(rec.width)}x${Math.round(rec.height)})px`);
});
}
10 changes: 3 additions & 7 deletions demo/nested.html
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ <h1>Nested grids demo</h1>
<a class="btn btn-primary" onClick="load(false)" href="#">Load</a>
<br><br>
<!-- grid will be added here -->
</div>d
</div>
<script src="events.js"></script>
<script type="text/javascript">
// NOTE: REAL apps would sanitize-html or DOMPurify before blinding setting innerHTML. see #2736
Expand Down Expand Up @@ -74,12 +74,8 @@ <h1>Nested grids demo</h1>
// create and load it all from JSON above
let grid = GridStack.addGrid(document.querySelector('.container-fluid'), options);

// add debug event handlers to each grid (no global set on parent yet)
let gridEls = GridStack.getElements('.grid-stack');
gridEls.forEach(gridEl => {
let grid = gridEl.gridstack;
addEvents(grid, grid.opts.id);
})
// add debug event handlers to main grid (new v12.1 handles sub-grids too)
addEvents(grid);

// setup drag drop behavior
let sidebarContent = [
Expand Down
9 changes: 5 additions & 4 deletions doc/CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +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/)*

- [12.0.0-dev (TBD)](#1200-dev-tbd)
- [12.1.0 (2024-04-23)](#1210-2024-04-23)
- [12.0.0 (2025-04-12)](#1200-2025-04-12)
- [11.5.1 (2025-03-23)](#1151-2025-03-23)
- [11.5.0 (2025-03-16)](#1150-2025-03-16)
Expand Down Expand Up @@ -125,12 +125,13 @@ Change log

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

## 12.0.0-dev (TBD)
* rem [#3022](https://github.com/gridstack/gridstack.js/pull/3022) removed ES5 support (IE doesn't support CSS vars needed now)
* rem [#3027](https://github.com/gridstack/gridstack.js/pull/3027) remove legacy code support for disableOneColumnMode, oneColumnSize, oneColumnModeDomSort
## 12.1.0 (2024-04-23)
* feat [#2671](https://github.com/gridstack/gridstack.js/issues/2671) subgrid now propagate events to topmost grid. Use `el.gridstackNode.grid` to know which (sub) grid.
* fix [#3028](https://github.com/gridstack/gridstack.js/pull/3028) `updateOptions()` no longer modifies passed in struct. only field we check are being handled too.
* fix [#3029](https://github.com/gridstack/gridstack.js/pull/3029) `resizeToContent()` fix for nested grid with content above
* fix [#3030](https://github.com/gridstack/gridstack.js/pull/3030) `resizeToContentCheck()` wasn't blocking _ignoreLayoutsNodeChange at end of the loop
* rem [#3022](https://github.com/gridstack/gridstack.js/pull/3022) removed ES5 support (IE doesn't support CSS vars needed now)
* rem [#3027](https://github.com/gridstack/gridstack.js/pull/3027) remove legacy code support for disableOneColumnMode, oneColumnSize, oneColumnModeDomSort

## 12.0.0 (2025-04-12)
* feat: [#2854](https://github.com/gridstack/gridstack.js/pull/2854) Removed dynamic stylesheet and migrated to CSS vars. Thank you [lmartorella](https://github.com/lmartorella)
Expand Down
28 changes: 18 additions & 10 deletions src/gridstack.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1579,7 +1579,11 @@ export class GridStack {
/** @internal */
protected _triggerEvent(type: string, data?: GridStackNode[]): GridStack {
const event = data ? new CustomEvent(type, { bubbles: false, detail: data }) : new Event(type);
this.el.dispatchEvent(event);
// check if we're nested, and if so call the outermost grid to trigger the event
// eslint-disable-next-line @typescript-eslint/no-this-alias
let grid: GridStack = this;
while (grid.parentGridNode) grid = grid.parentGridNode.grid;
grid.el.dispatchEvent(event);
return this;
}

Expand Down Expand Up @@ -2374,9 +2378,7 @@ export class GridStack {
/** called when item starts moving/resizing */
const onStartMoving = (event: Event, ui: DDUIData) => {
// trigger any 'dragstart' / 'resizestart' manually
if (this._gsEventHandler[event.type]) {
this._gsEventHandler[event.type](event, event.target);
}
this.triggerEvent(event, event.target as GridItemHTMLElement);
cellWidth = this.cellWidth();
cellHeight = this.getCellHeight(true); // force pixels for calculations

Expand Down Expand Up @@ -2422,9 +2424,7 @@ export class GridStack {
// move to new placeholder location
this._writePosAttr(target, node);
}
if (this._gsEventHandler[event.type]) {
this._gsEventHandler[event.type](event, target);
}
this.triggerEvent(event, target);
}
// @ts-ignore
this._extraDragRow = 0;// @ts-ignore
Expand Down Expand Up @@ -2603,9 +2603,17 @@ export class GridStack {
if (!node._sidebarOrig) {
this._writePosAttr(target, node);
}
if (this._gsEventHandler[event.type]) {
this._gsEventHandler[event.type](event, target);
}
this.triggerEvent(event, target);
}
}

/** call given event callback on our main top-most grid (if we're nested) */
protected triggerEvent(event: Event, target: GridItemHTMLElement) {
// eslint-disable-next-line @typescript-eslint/no-this-alias
let grid: GridStack = this;
while (grid.parentGridNode) grid = grid.parentGridNode.grid;
if (grid._gsEventHandler[event.type]) {
grid._gsEventHandler[event.type](event, target);
}
}

Expand Down