Skip to content

Commit c913592

Browse files
authored
Merge branch 'master' into patch-1
2 parents a100e6c + e459312 commit c913592

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

46 files changed

+1758
-1799
lines changed

README.md

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ Join us on Slack: [https://gridstackjs.slack.com](https://join.slack.com/t/grids
5050
- [Migrating to v11](#migrating-to-v11)
5151
- [jQuery Application](#jquery-application)
5252
- [Changes](#changes)
53+
- [Usage Trend](#usage-trend)
5354
- [The Team](#the-team)
5455

5556
<!-- END doctoc generated TOC please keep comment here to allow auto update -->
@@ -463,9 +464,14 @@ breaking change:
463464
464465
## Migrating to v11
465466
466-
All instances of `el.innerHTML = 'some content'` have been removed for security reason as it opens up some potential for accidental XSS. we now create DIV directly or use `el.textContent = w.content` for `GridStackWidget.content` field.
467+
* All instances of `el.innerHTML = 'some content'` have been removed for security reason as it opens up some potential for accidental XSS.
468+
469+
* Side panel drag&drop complete rewrite.
470+
471+
* new lazy loading option
472+
473+
**Breaking change:**
467474
468-
breaking change:
469475
* if you code relies on `GridStackWidget.content` with real HTML (like a few demos) it is up to you to do this:
470476
```ts
471477
// NOTE: REAL apps would sanitize-html or DOMPurify before blinding setting innerHTML. see #2736
@@ -476,6 +482,15 @@ GridStack.renderCB = function(el, w) {
476482
* V11 add new `GridStack.renderCB` that is called for you to create the widget content (entire GridStackWidget is passed so you can use id or some other field as logic) while GS creates the 2 needed parent divs + classes, unlike `GridStack.addRemoveCB` which doesn't create anything for you. Both can be handy for Angular/React/Vue frameworks.
477483
* `addWidget(w: GridStackWidget)` is now the only supported format, no more string content passing. You will need to create content yourself (`Util.createWidgetDivs()` can be used to create parent divs) then call `makeWidget(el)` instead.
478484
485+
**Potential breaking change:**
486+
487+
* BIG overall to how sidepanel helper drag&drop is done:
488+
1. `clone()` helper is now passed full HTML element dragged, not an event on `grid-stack-item-content` so can clone or set attr at the top.
489+
2. use any class/structure you want for side panel items (see two.html)
490+
3. `GridStack.setupDragIn()` now support associating a `GridStackWidget` for each sidepanel that will be used to define what to create on drop!
491+
4. if no `GridStackWidget` is defined, the helper will now be inserted as is, and NOT original sidepanel item.
492+
5. support DOM gs- attr as well as gridstacknode JSON (see two.html) alternatives.
493+
479494
# jQuery Application
480495
481496
This is **old and no longer apply to v6+**. You'll need to use v5.1.1 and before

angular/README.md

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,11 @@ HTML
129129

130130
```html
131131
<gridstack [options]="gridOptions" (changeCB)="onChange($event)">
132+
<!-- Angular 17+ -->
133+
@for (n of items; track n.id) {
134+
<gridstack-item [options]="n">Item {{n.id}}</gridstack-item>
135+
}
136+
<!-- Angular 16 -->
132137
<gridstack-item *ngFor="let n of items; trackBy: identify" [options]="n"> Item {{n.id}} </gridstack-item>
133138
</gridstack>
134139
```
@@ -162,7 +167,7 @@ public identify(index: number, w: GridStackWidget) {
162167

163168
You can see a fuller example at [app.component.ts](projects/demo/src/app/app.component.ts)
164169

165-
to build the demo, go to [angular/projects/demo](projects/demo/) and run `yarn` + `yarn upgrade` (ng18 seem to have introduced some conflicts) + `yarn start` and navigate to `http://localhost:4200/`
170+
to build the demo, go to [angular/projects/demo](projects/demo/) and run `yarn` + `yarn start` and navigate to `http://localhost:4200/`
166171

167172
Code started shipping with v8.1.2+ in `dist/angular` for people to use directly and is an angular module! (source code under `dist/angular/src`)
168173

angular/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
"@angular/platform-browser": "~18.2.6",
1919
"@angular/platform-browser-dynamic": "~18.2.6",
2020
"@angular/router": "~18.2.6",
21-
"gridstack": "^10.3.1",
21+
"gridstack": "^11.0.1",
2222
"rxjs": "~7.5.0",
2323
"tslib": "^2.3.0",
2424
"zone.js": "~0.14"

angular/projects/demo/src/app/app.component.html

Lines changed: 103 additions & 92 deletions
Original file line numberDiff line numberDiff line change
@@ -9,110 +9,121 @@
99
<button (click)="onShow(5)" [class.active]="show===5">Component Dynamic</button>
1010
<button (click)="onShow(6)" [class.active]="show===6">Nested Grid</button>
1111
<button (click)="onShow(7)" [class.active]="show===7">Two Grids + sidebar</button>
12-
<button (click)="onShow(8)" [class.active]="show===8">Leak Test</button>
12+
<button (click)="onShow(8)" [class.active]="show===8">Lazy Load</button>
13+
<button (click)="onShow(9)" [class.active]="show===9">Leak Test</button>
1314
</div>
1415

1516
<div class="test-container">
16-
<angular-simple-test *ngIf="show===0"></angular-simple-test>
17-
<angular-ng-for-test *ngIf="show===1"></angular-ng-for-test>
18-
<angular-ng-for-cmd-test *ngIf="show===2"></angular-ng-for-cmd-test>
19-
20-
<div *ngIf="show===3">
21-
<p><b>COMPONENT template</b>: using DOM template to use components statically</p>
22-
<button (click)="add()">add item</button>
23-
<button (click)="delete()">remove item</button>
24-
<button (click)="modify()">modify item</button>
25-
<button (click)="newLayout()">new layout</button>
26-
<gridstack [options]="gridOptions" (changeCB)="onChange($event)" (resizeStopCB)="onResizeStop($event)">
27-
<gridstack-item gs-x="1" gs-y="0">item 1</gridstack-item>
28-
<gridstack-item gs-x="3" gs-y="0" gs-w="2">item 2 wide</gridstack-item>
29-
</gridstack>
30-
</div>
31-
32-
<div *ngIf="show===4">
33-
<p><b>COMPONENT ngFor</b>: Most complete example that uses Component wrapper for grid and gridItem</p>
34-
<button (click)="addNgFor()">add item</button>
35-
<button (click)="deleteNgFor()">remove item</button>
36-
<button (click)="modifyNgFor()">modify item</button>
37-
<button (click)="newLayoutNgFor()">new layout</button>
38-
<gridstack [options]="gridOptions" (changeCB)="onChange($event)" (resizeStopCB)="onResizeStop($event)">
39-
<gridstack-item *ngFor="let n of items; trackBy: identify" [options]="n">
40-
</gridstack-item>
41-
</gridstack>
42-
</div>
43-
44-
<div *ngIf="show===5">
45-
<p><b>COMPONENT dynamic</b>: Best example that uses Component wrapper and dynamic grid creation (drag between grids, from toolbar, etc...)</p>
46-
<button (click)="add()">add item</button>
47-
<button (click)="delete()">remove item</button>
48-
<button (click)="modify()">modify item</button>
49-
<button (click)="newLayout()">new layout</button>
50-
<button (click)="saveGrid()">Save</button>
51-
<button (click)="clearGrid()">Clear</button>
52-
<button (click)="loadGrid()">Load</button>
53-
<gridstack [options]="gridOptionsFull" (changeCB)="onChange($event)" (resizeStopCB)="onResizeStop($event)">
54-
</gridstack>
55-
</div>
56-
57-
58-
<div *ngIf="show===6">
59-
<p><b>Nested Grid</b>: shows nested component grids, like nested.html demo but with Ng Components</p>
60-
<button (click)="add()">add item</button>
61-
<button (click)="delete()">remove item</button>
62-
<button (click)="modify()">modify item</button>
63-
<button (click)="newLayout()">new layout</button>
64-
<button (click)="saveGrid()">Save</button>
65-
<button (click)="clearGrid()">Clear</button>
66-
<button (click)="loadGrid()">Load</button>
67-
<!-- TODO: addGrid() in code for testing instead ? -->
68-
<gridstack [options]="nestedGridOptions" (changeCB)="onChange($event)" (resizeStopCB)="onResizeStop($event)">
69-
<div empty-content>Add items here or reload the grid</div>
70-
</gridstack>
71-
</div>
17+
@if (show===0) {
18+
<angular-simple-test></angular-simple-test>
19+
} @else if (show===1) {
20+
<angular-ng-for-test></angular-ng-for-test>
21+
} @else if (show===2) {
22+
<angular-ng-for-cmd-test></angular-ng-for-cmd-test>
23+
} @else if (show===3) {
24+
<div>
25+
<p><b>COMPONENT template</b>: using DOM template to use components statically</p>
26+
<button (click)="add()">add item</button>
27+
<button (click)="delete()">remove item</button>
28+
<button (click)="modify()">modify item</button>
29+
<button (click)="newLayout()">new layout</button>
30+
<gridstack [options]="gridOptions" (changeCB)="onChange($event)" (resizeStopCB)="onResizeStop($event)">
31+
<gridstack-item gs-x="1" gs-y="0">item 1</gridstack-item>
32+
<gridstack-item gs-x="3" gs-y="0" gs-w="2">item 2 wide</gridstack-item>
33+
</gridstack>
34+
</div>
35+
} @else if (show===4) {
36+
<div>
37+
<p><b>COMPONENT ngFor</b>: Most complete example that uses Component wrapper for grid and gridItem</p>
38+
<button (click)="addNgFor()">add item</button>
39+
<button (click)="deleteNgFor()">remove item</button>
40+
<button (click)="modifyNgFor()">modify item</button>
41+
<button (click)="newLayoutNgFor()">new layout</button>
42+
<gridstack [options]="gridOptions" (changeCB)="onChange($event)" (resizeStopCB)="onResizeStop($event)">
43+
@for (n of items; track n.id) {
44+
<gridstack-item [options]="n"></gridstack-item>
45+
}
46+
</gridstack>
47+
</div>
48+
} @else if (show===5) {
49+
<div>
50+
<p><b>COMPONENT dynamic</b>: Best example that uses Component wrapper and dynamic grid creation (drag between grids, from toolbar, etc...)</p>
51+
<button (click)="add()">add item</button>
52+
<button (click)="delete()">remove item</button>
53+
<button (click)="modify()">modify item</button>
54+
<button (click)="newLayout()">new layout</button>
55+
<button (click)="saveGrid()">Save</button>
56+
<button (click)="clearGrid()">Clear</button>
57+
<button (click)="loadGrid()">Load</button>
58+
<gridstack [options]="gridOptionsFull" (changeCB)="onChange($event)" (resizeStopCB)="onResizeStop($event)">
59+
</gridstack>
60+
</div>
61+
} @else if (show===6) {
62+
<div>
63+
<p><b>Nested Grid</b>: shows nested component grids, like nested.html demo but with Ng Components</p>
64+
<button (click)="add()">add item</button>
65+
<button (click)="delete()">remove item</button>
66+
<button (click)="modify()">modify item</button>
67+
<button (click)="newLayout()">new layout</button>
68+
<button (click)="saveGrid()">Save</button>
69+
<button (click)="clearGrid()">Clear</button>
70+
<button (click)="loadGrid()">Load</button>
71+
<!-- add .grid-stack-item for acceptWidgets:true -->
72+
<div class="sidebar-item grid-stack-item">Drag nested</div>
73+
<div class="sidebar-item grid-stack-item">Comp N nested</div>
7274

73-
<div *ngIf="show===7">
74-
<p><b>two.html</b>: shows multiple grids and outside drag&drop</p>
75-
<div class="row">
76-
<div class="col-md-3">
77-
<div class="sidebar">
78-
<!-- will size to match content -->
79-
<div class="grid-stack-item">
80-
<div class="grid-stack-item-content">Drag me</div>
75+
<!-- TODO: addGrid() in code for testing instead ? -->
76+
<gridstack [options]="nestedGridOptions" (changeCB)="onChange($event)" (resizeStopCB)="onResizeStop($event)">
77+
<div empty-content>Add items here or reload the grid</div>
78+
</gridstack>
79+
</div>
80+
} @else if (show===7) {
81+
<div>
82+
<p><b>two.html</b>: shows multiple grids, sidebar creating Components</p>
83+
<div class="row">
84+
<div class="col-md-6">
85+
<div class="sidebar">
86+
<div class="sidebar-item grid-stack-item">will be A</div>
87+
<div class="sidebar-item grid-stack-item">will be B max=3</div>
88+
</div>
8189
</div>
82-
<!-- manually force a drop size of 2x1 -->
83-
<div class="grid-stack-item" gs-w="2" gs-h="1" gs-max-w="3">
84-
<div class="grid-stack-item-content">2x1, max=3</div>
90+
<div class="col-md-6">
91+
<div class="trash" id="trash">
92+
</div>
8593
</div>
8694
</div>
87-
</div>
88-
<div class="col-md-9">
89-
<div class="trash" id="trash">
95+
<div class="row" style="margin-top: 20px">
96+
<div class="col-md-6">
97+
<gridstack [options]="twoGridOpt1"></gridstack>
98+
</div>
99+
<div class="col-md-6">
100+
<gridstack [options]="twoGridOpt2"></gridstack>
101+
</div>
90102
</div>
91103
</div>
92-
</div>
93-
<div class="row" style="margin-top: 20px">
94-
<div class="col-md-6">
95-
<gridstack [options]="twoGridOpt1"></gridstack>
104+
} @else if (show===8) {
105+
<div>
106+
<p>delay loading of components</p>
107+
<div style="height: 120px; overflow-y: auto">
108+
<gridstack [options]="gridOptionsDelay"></gridstack>
109+
</div>
96110
</div>
97-
<div class="col-md-6">
98-
<gridstack [options]="twoGridOpt2"></gridstack>
111+
} @else if (show===9) {
112+
<div>
113+
<p>load() + clear() to memory leak test between the two</p>
114+
<button (click)="clearGrid()">Clear</button>
115+
<button (click)="load(sub0)">load</button>
116+
<button (click)="load(sub2)">load 2</button>
117+
<gridstack [options]="gridOptions"></gridstack>
99118
</div>
100-
</div>
101-
</div>
102-
103-
<div *ngIf="show===8">
104-
<p>load() + clear() to memory leak test between the two</p>
105-
<button (click)="clearGrid()">Clear</button>
106-
<button (click)="load(sub0)">load</button>
107-
<button (click)="load(sub2)">load 2</button>
108-
<gridstack [options]="gridOptions"></gridstack>
109-
</div>
110119

111-
<div class="grid-container"></div>
120+
<div class="grid-container"></div>
112121

113-
<div class="text-container" *ngIf="show!=8">
114-
<textarea #origTextArea cols="50" rows="50" readonly="readonly"></textarea>
115-
<textarea #textArea cols="50" rows="50" readonly="readonly"></textarea>
116-
</div>
122+
} @else if (show!=8) {
123+
<div class="text-container">
124+
<textarea #origTextArea cols="50" rows="50" readonly="readonly"></textarea>
125+
<textarea #textArea cols="50" rows="50" readonly="readonly"></textarea>
126+
</div>
127+
}
117128

118129
</div>

angular/projects/demo/src/app/app.component.ts

Lines changed: 33 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { AngularSimpleComponent } from './simple';
44
import { AngularNgForTestComponent } from './ngFor';
55
import { AngularNgForCmdTestComponent } from './ngFor_cmd';
66

7-
// NOTE: local testing of file
7+
// TEST: local testing of file
88
// import { GridstackComponent, NgGridStackOptions, NgGridStackWidget, elementCB, gsCreateNgComponents, nodesCB } from './gridstack.component';
99
import { GridstackComponent, NgGridStackOptions, NgGridStackWidget, elementCB, gsCreateNgComponents, nodesCB } from 'gridstack/dist/angular';
1010

@@ -46,6 +46,13 @@ export class AppComponent implements OnInit {
4646
children: this.sub0,
4747
}
4848

49+
public lazyChildren: NgGridStackWidget[] = [];
50+
public gridOptionsDelay: NgGridStackOptions = {
51+
...this.gridOptions,
52+
lazyLoad: true,
53+
children: this.lazyChildren,
54+
}
55+
4956
// nested grid options
5057
private subOptions: GridStackOptions = {
5158
cellHeight: 50, // should be 50 - top/bottom
@@ -55,17 +62,20 @@ export class AppComponent implements OnInit {
5562
};
5663
public sub1: NgGridStackWidget[] = [ {x:0, y:0, selector:'app-a'}, {x:1, y:0, selector:'app-b'}, {x:2, y:0, selector:'app-c'}, {x:3, y:0}, {x:0, y:1}, {x:1, y:1}];
5764
public sub2: NgGridStackWidget[] = [ {x:0, y:0}, {x:0, y:1, w:2}];
65+
public sub3: NgGridStackWidget = { selector: 'app-n', w:2, h:2, subGridOpts: { children: [{selector: 'app-a'}, {selector: 'app-b', y:0, x:1}]}};
5866
private subChildren: NgGridStackWidget[] = [
5967
{x:0, y:0, content: 'regular item'},
60-
{x:1, y:0, w:4, h:4, subGridOpts: {children: this.sub1, class: 'sub1', ...this.subOptions}},
61-
{x:5, y:0, w:3, h:4, subGridOpts: {children: this.sub2, class: 'sub2', ...this.subOptions}},
68+
{x:1, y:0, w:4, h:4, subGridOpts: {children: this.sub1}},
69+
// {x:5, y:0, w:3, h:4, subGridOpts: {children: this.sub2}},
70+
this.sub3,
6271
]
6372
public nestedGridOptions: NgGridStackOptions = { // main grid options
6473
cellHeight: 50,
6574
margin: 5,
6675
minRow: 2, // don't collapse when empty
6776
acceptWidgets: true,
68-
children: this.subChildren
77+
subGridOpts: this.subOptions, // all sub grids will default to those
78+
children: this.subChildren,
6979
};
7080
public twoGridOpt1: NgGridStackOptions = {
7181
column: 6,
@@ -76,20 +86,31 @@ export class AppComponent implements OnInit {
7686
acceptWidgets: true,
7787
float: true,
7888
children: [
79-
{x: 0, y: 0, w: 2, h: 2},
80-
{x: 3, y: 1, h: 2},
89+
{x: 0, y: 0, w: 2, h: 2, selector: 'app-a'},
90+
{x: 3, y: 1, h: 2, selector: 'app-b'},
8191
{x: 4, y: 1},
8292
{x: 2, y: 3, w: 3, maxW: 3, id: 'special', content: 'has maxW=3'},
8393
]
8494
};
8595
public twoGridOpt2: NgGridStackOptions = { ...this.twoGridOpt1, float: false }
8696
private serializedData?: NgGridStackOptions;
8797

98+
// sidebar content to create storing the Widget description to be used on drop
99+
public sidebarContent6: NgGridStackWidget[] = [
100+
{ w:2, h:2, subGridOpts: { children: [{content: 'nest 1'}, {content: 'nest 2'}]}},
101+
this.sub3,
102+
];
103+
public sidebarContent7: NgGridStackWidget[] = [
104+
{selector: 'app-a'},
105+
{selector: 'app-b', w:2, maxW: 3},
106+
];
107+
88108
constructor() {
109+
for (let y = 0; y <= 5; y++) this.lazyChildren.push({x:0, y, id:String(y), selector: y%2 ? 'app-b' : 'app-a'});
110+
89111
// give them content and unique id to make sure we track them during changes below...
90112
[...this.items, ...this.subChildren, ...this.sub1, ...this.sub2, ...this.sub0].forEach((w: NgGridStackWidget) => {
91-
if (!w.selector && !w.content && !w.subGridOpts) w.content = `item ${ids}`;
92-
w.id = String(ids++);
113+
if (!w.selector && !w.content && !w.subGridOpts) w.content = `item ${ids++}`;
93114
});
94115
}
95116

@@ -126,9 +147,11 @@ export class AppComponent implements OnInit {
126147
case 3: data = this.gridComp?.grid?.save(true, true); break;
127148
case 4: data = this.items; break;
128149
case 5: data = this.gridOptionsFull; break;
129-
case 6: data = this.nestedGridOptions; break;
150+
case 6: data = this.nestedGridOptions;
151+
GridStack.setupDragIn('.sidebar-item', undefined, this.sidebarContent6);
152+
break;
130153
case 7: data = this.twoGridOpt1;
131-
GridStack.setupDragIn('.sidebar .grid-stack-item', { appendTo: 'body', helper: 'clone' });
154+
GridStack.setupDragIn('.sidebar-item', undefined, this.sidebarContent7);
132155
break;
133156
}
134157
if (this.origTextEl) this.origTextEl.nativeElement.value = JSON.stringify(data, null, ' ');

0 commit comments

Comments
 (0)