gridstack 8.1.1 → 8.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (158) hide show
  1. package/README.md +12 -11
  2. package/angular/.editorconfig +16 -0
  3. package/angular/.vscode/extensions.json +4 -0
  4. package/angular/.vscode/launch.json +20 -0
  5. package/angular/.vscode/tasks.json +42 -0
  6. package/{dist/ng → angular}/README.md +170 -154
  7. package/angular/README_build.md +27 -0
  8. package/angular/angular.json +132 -0
  9. package/angular/package.json +40 -0
  10. package/angular/projects/demo/.browserslistrc +16 -0
  11. package/angular/projects/demo/src/app/app.component.css +11 -0
  12. package/angular/projects/demo/src/app/app.component.html +78 -0
  13. package/angular/projects/demo/src/app/app.component.spec.ts +25 -0
  14. package/angular/projects/demo/src/app/app.component.ts +208 -0
  15. package/angular/projects/demo/src/app/app.module.ts +39 -0
  16. package/angular/projects/demo/src/app/dummy.component.ts +35 -0
  17. package/angular/projects/demo/src/app/ngFor.ts +131 -0
  18. package/angular/projects/demo/src/app/ngFor_cmd.ts +106 -0
  19. package/angular/projects/demo/src/app/simple.ts +46 -0
  20. package/angular/projects/demo/src/assets/.gitkeep +0 -0
  21. package/angular/projects/demo/src/environments/environment.prod.ts +3 -0
  22. package/angular/projects/demo/src/environments/environment.ts +16 -0
  23. package/angular/projects/demo/src/favicon.ico +0 -0
  24. package/angular/projects/demo/src/index.html +13 -0
  25. package/angular/projects/demo/src/main.ts +12 -0
  26. package/angular/projects/demo/src/polyfills.ts +53 -0
  27. package/angular/projects/demo/src/styles.css +4 -0
  28. package/angular/projects/demo/src/test.ts +26 -0
  29. package/angular/projects/demo/tsconfig.app.json +15 -0
  30. package/angular/projects/demo/tsconfig.spec.json +18 -0
  31. package/angular/projects/lib/README.md +24 -0
  32. package/angular/projects/lib/ng-package.json +7 -0
  33. package/angular/projects/lib/package.json +11 -0
  34. package/angular/projects/lib/src/lib/base-widget.ts +28 -0
  35. package/angular/projects/lib/src/lib/gridstack-item.component.ts +78 -0
  36. package/angular/projects/lib/src/lib/gridstack.component.ts +287 -0
  37. package/angular/projects/lib/src/lib/gridstack.module.ts +32 -0
  38. package/angular/projects/lib/src/public-api.ts +8 -0
  39. package/angular/projects/lib/src/test.ts +27 -0
  40. package/angular/projects/lib/tsconfig.lib.json +15 -0
  41. package/angular/projects/lib/tsconfig.lib.prod.json +10 -0
  42. package/angular/projects/lib/tsconfig.spec.json +17 -0
  43. package/dist/angular/README.md +170 -0
  44. package/dist/angular/esm2020/gridstack-angular.mjs +5 -0
  45. package/dist/angular/esm2020/lib/base-widget.mjs +30 -0
  46. package/dist/angular/esm2020/lib/gridstack-item.component.mjs +68 -0
  47. package/dist/angular/esm2020/lib/gridstack.component.mjs +278 -0
  48. package/dist/angular/esm2020/lib/gridstack.module.mjs +39 -0
  49. package/dist/angular/esm2020/public-api.mjs +8 -0
  50. package/dist/angular/fesm2015/gridstack-angular.mjs +418 -0
  51. package/dist/angular/fesm2015/gridstack-angular.mjs.map +1 -0
  52. package/dist/angular/fesm2020/gridstack-angular.mjs +413 -0
  53. package/dist/angular/fesm2020/gridstack-angular.mjs.map +1 -0
  54. package/dist/angular/index.d.ts +5 -0
  55. package/dist/angular/lib/base-widget.d.ts +16 -0
  56. package/dist/{ng → angular/lib}/gridstack-item.component.d.ts +37 -29
  57. package/dist/{ng → angular/lib}/gridstack.component.d.ts +129 -118
  58. package/dist/angular/lib/gridstack.module.d.ts +10 -0
  59. package/dist/angular/package.json +31 -0
  60. package/dist/angular/public-api.d.ts +4 -0
  61. package/dist/angular/src/base-widget.ts +28 -0
  62. package/dist/angular/src/gridstack-item.component.ts +78 -0
  63. package/dist/angular/src/gridstack.component.ts +287 -0
  64. package/dist/angular/src/gridstack.module.ts +32 -0
  65. package/dist/dd-base-impl.d.ts +1 -1
  66. package/dist/dd-base-impl.js +1 -1
  67. package/dist/dd-base-impl.js.map +1 -1
  68. package/dist/dd-draggable.d.ts +1 -1
  69. package/dist/dd-draggable.js +1 -1
  70. package/dist/dd-draggable.js.map +1 -1
  71. package/dist/dd-droppable.d.ts +1 -1
  72. package/dist/dd-droppable.js +1 -1
  73. package/dist/dd-droppable.js.map +1 -1
  74. package/dist/dd-element.d.ts +1 -1
  75. package/dist/dd-element.js +1 -1
  76. package/dist/dd-element.js.map +1 -1
  77. package/dist/dd-gridstack.d.ts +1 -1
  78. package/dist/dd-gridstack.js +1 -1
  79. package/dist/dd-gridstack.js.map +1 -1
  80. package/dist/dd-manager.d.ts +1 -1
  81. package/dist/dd-manager.js +1 -1
  82. package/dist/dd-manager.js.map +1 -1
  83. package/dist/dd-resizable-handle.d.ts +1 -1
  84. package/dist/dd-resizable-handle.js +1 -1
  85. package/dist/dd-resizable-handle.js.map +1 -1
  86. package/dist/dd-resizable.d.ts +1 -1
  87. package/dist/dd-resizable.js +1 -1
  88. package/dist/dd-resizable.js.map +1 -1
  89. package/dist/dd-touch.d.ts +1 -1
  90. package/dist/dd-touch.js +1 -1
  91. package/dist/dd-touch.js.map +1 -1
  92. package/dist/es5/dd-base-impl.d.ts +1 -1
  93. package/dist/es5/dd-base-impl.js +1 -1
  94. package/dist/es5/dd-base-impl.js.map +1 -1
  95. package/dist/es5/dd-draggable.d.ts +1 -1
  96. package/dist/es5/dd-draggable.js +1 -1
  97. package/dist/es5/dd-draggable.js.map +1 -1
  98. package/dist/es5/dd-droppable.d.ts +1 -1
  99. package/dist/es5/dd-droppable.js +1 -1
  100. package/dist/es5/dd-droppable.js.map +1 -1
  101. package/dist/es5/dd-element.d.ts +1 -1
  102. package/dist/es5/dd-element.js +1 -1
  103. package/dist/es5/dd-element.js.map +1 -1
  104. package/dist/es5/dd-gridstack.d.ts +1 -1
  105. package/dist/es5/dd-gridstack.js +1 -1
  106. package/dist/es5/dd-gridstack.js.map +1 -1
  107. package/dist/es5/dd-manager.d.ts +1 -1
  108. package/dist/es5/dd-manager.js +1 -1
  109. package/dist/es5/dd-manager.js.map +1 -1
  110. package/dist/es5/dd-resizable-handle.d.ts +1 -1
  111. package/dist/es5/dd-resizable-handle.js +1 -1
  112. package/dist/es5/dd-resizable-handle.js.map +1 -1
  113. package/dist/es5/dd-resizable.d.ts +1 -1
  114. package/dist/es5/dd-resizable.js +1 -1
  115. package/dist/es5/dd-resizable.js.map +1 -1
  116. package/dist/es5/dd-touch.d.ts +1 -1
  117. package/dist/es5/dd-touch.js +1 -1
  118. package/dist/es5/dd-touch.js.map +1 -1
  119. package/dist/es5/gridstack-all.js +1 -1
  120. package/dist/es5/gridstack-all.js.LICENSE.txt +1 -1
  121. package/dist/es5/gridstack-all.js.map +1 -1
  122. package/dist/es5/gridstack-engine.d.ts +1 -1
  123. package/dist/es5/gridstack-engine.js +1 -1
  124. package/dist/es5/gridstack-engine.js.map +1 -1
  125. package/dist/es5/gridstack-poly.js +1 -1
  126. package/dist/es5/gridstack.d.ts +8 -5
  127. package/dist/es5/gridstack.js +13 -7
  128. package/dist/es5/gridstack.js.map +1 -1
  129. package/dist/es5/types.d.ts +1 -1
  130. package/dist/es5/types.js +1 -1
  131. package/dist/es5/types.js.map +1 -1
  132. package/dist/es5/utils.d.ts +1 -1
  133. package/dist/es5/utils.js +1 -1
  134. package/dist/es5/utils.js.map +1 -1
  135. package/dist/gridstack-all.js +1 -1
  136. package/dist/gridstack-all.js.LICENSE.txt +1 -1
  137. package/dist/gridstack-all.js.map +1 -1
  138. package/dist/gridstack-engine.d.ts +1 -1
  139. package/dist/gridstack-engine.js +1 -1
  140. package/dist/gridstack-engine.js.map +1 -1
  141. package/dist/gridstack.css +1 -1
  142. package/dist/gridstack.d.ts +8 -5
  143. package/dist/gridstack.js +13 -7
  144. package/dist/gridstack.js.map +1 -1
  145. package/dist/src/gridstack-extra.scss +3 -3
  146. package/dist/src/gridstack.scss +1 -1
  147. package/dist/types.d.ts +1 -1
  148. package/dist/types.js +1 -1
  149. package/dist/types.js.map +1 -1
  150. package/dist/utils.d.ts +1 -1
  151. package/dist/utils.js +1 -1
  152. package/dist/utils.js.map +1 -1
  153. package/doc/CHANGES.md +11 -1
  154. package/package.json +3 -3
  155. package/dist/ng/gridstack-item.component.js +0 -65
  156. package/dist/ng/gridstack-item.component.js.map +0 -1
  157. package/dist/ng/gridstack.component.js +0 -245
  158. package/dist/ng/gridstack.component.js.map +0 -1
@@ -0,0 +1,40 @@
1
+ {
2
+ "name": "gridstack-lib",
3
+ "version": "0.0.0",
4
+ "scripts": {
5
+ "ng": "ng",
6
+ "start": "ng serve",
7
+ "build": "ng build",
8
+ "watch": "ng build --watch --configuration development",
9
+ "test": "ng test"
10
+ },
11
+ "private": true,
12
+ "dependencies": {
13
+ "@angular/animations": "^14.2.0",
14
+ "@angular/common": "^14.2.0",
15
+ "@angular/compiler": "^14.2.0",
16
+ "@angular/core": "^14.2.0",
17
+ "@angular/forms": "^14.2.0",
18
+ "@angular/platform-browser": "^14.2.0",
19
+ "@angular/platform-browser-dynamic": "^14.2.0",
20
+ "@angular/router": "^14.2.0",
21
+ "gridstack": "^8.1.2",
22
+ "rxjs": "~7.5.0",
23
+ "tslib": "^2.3.0",
24
+ "zone.js": "~0.11.4"
25
+ },
26
+ "devDependencies": {
27
+ "@angular-devkit/build-angular": "^14.2.11",
28
+ "@angular/cli": "~14.2.10",
29
+ "@angular/compiler-cli": "^14.2.0",
30
+ "@types/jasmine": "~4.0.0",
31
+ "jasmine-core": "~4.3.0",
32
+ "karma": "~6.4.0",
33
+ "karma-chrome-launcher": "~3.1.0",
34
+ "karma-coverage": "~2.2.0",
35
+ "karma-jasmine": "~5.1.0",
36
+ "karma-jasmine-html-reporter": "~2.0.0",
37
+ "ng-packagr": "^14.2.0",
38
+ "typescript": "~4.7.2"
39
+ }
40
+ }
@@ -0,0 +1,16 @@
1
+ # This file is used by the build system to adjust CSS and JS output to support the specified browsers below.
2
+ # For additional information regarding the format and rule options, please see:
3
+ # https://github.com/browserslist/browserslist#queries
4
+
5
+ # For the full list of supported browsers by the Angular framework, please see:
6
+ # https://angular.io/guide/browser-support
7
+
8
+ # You can see what browsers were selected by your queries by running:
9
+ # npx browserslist
10
+
11
+ last 1 Chrome version
12
+ last 1 Firefox version
13
+ last 2 Edge major versions
14
+ last 2 Safari major versions
15
+ last 2 iOS major versions
16
+ Firefox ESR
@@ -0,0 +1,11 @@
1
+ .test-container {
2
+ margin-top: 30px;
3
+ }
4
+ button.active {
5
+ color: #fff;
6
+ background-color: #007bff;
7
+ }
8
+
9
+ .text-container {
10
+ display: flex;
11
+ }
@@ -0,0 +1,78 @@
1
+
2
+ <div class="button-container">
3
+ <p class="pick-info">Pick a demo to load:</p>
4
+ <button (click)="onShow(0)" [class.active]="show===0">Simple</button>
5
+ <button (click)="onShow(1)" [class.active]="show===1">ngFor case</button>
6
+ <button (click)="onShow(2)" [class.active]="show===2">ngFor custom command</button>
7
+ <button (click)="onShow(3)" [class.active]="show===3">Component HTML template</button>
8
+ <button (click)="onShow(4)" [class.active]="show===4">Component ngFor</button>
9
+ <button (click)="onShow(5)" [class.active]="show===5">Component Dynamic</button>
10
+ <button (click)="onShow(6)" [class.active]="show===6">Nested Grid</button>
11
+ </div>
12
+
13
+ <div class="test-container">
14
+ <angular-simple-test *ngIf="show===0"></angular-simple-test>
15
+ <angular-ng-for-test *ngIf="show===1"></angular-ng-for-test>
16
+ <angular-ng-for-cmd-test *ngIf="show===2"></angular-ng-for-cmd-test>
17
+
18
+ <div *ngIf="show===3">
19
+ <p><b>COMPONENT template</b>: using DOM template to use components statically</p>
20
+ <button (click)="add()">add item</button>
21
+ <button (click)="delete()">remove item</button>
22
+ <button (click)="modify()">modify item</button>
23
+ <button (click)="newLayout()">new layout</button>
24
+ <gridstack [options]="gridOptions" (changeCB)="onChange($event)" (resizeStopCB)="onResizeStop($event)">
25
+ <gridstack-item gs-x="1" gs-y="0">item 1</gridstack-item>
26
+ <gridstack-item gs-x="3" gs-y="0" gs-w="2">item 2 wide</gridstack-item>
27
+ </gridstack>
28
+ </div>
29
+
30
+ <div *ngIf="show===4">
31
+ <p><b>COMPONENT ngFor</b>: Most complete example that uses Component wrapper for grid and gridItem</p>
32
+ <button (click)="addNgFor()">add item</button>
33
+ <button (click)="deleteNgFor()">remove item</button>
34
+ <button (click)="modifyNgFor()">modify item</button>
35
+ <button (click)="newLayoutNgFor()">new layout</button>
36
+ <gridstack [options]="gridOptions" (changeCB)="onChange($event)" (resizeStopCB)="onResizeStop($event)">
37
+ <gridstack-item *ngFor="let n of items; trackBy: identify" [options]="n">
38
+ </gridstack-item>
39
+ </gridstack>
40
+ </div>
41
+
42
+ <div *ngIf="show===5">
43
+ <p><b>COMPONENT dynamic</b>: Best example that uses Component wrapper and dynamic grid creation (drag between grids, from toolbar, etc...)</p>
44
+ <button (click)="add()">add item</button>
45
+ <button (click)="delete()">remove item</button>
46
+ <button (click)="modify()">modify item</button>
47
+ <button (click)="newLayout()">new layout</button>
48
+ <button (click)="saveGrid()">Save</button>
49
+ <button (click)="clearGrid()">Clear</button>
50
+ <button (click)="loadGrid()">Load</button>
51
+ <gridstack [options]="gridOptionsFull" (changeCB)="onChange($event)" (resizeStopCB)="onResizeStop($event)">
52
+ </gridstack>
53
+ </div>
54
+
55
+
56
+ <div *ngIf="show===6">
57
+ <p><b>Nested Grid</b>: shows nested component grids, like nested.html demo but with Ng Components</p>
58
+ <button (click)="add()">add item</button>
59
+ <button (click)="delete()">remove item</button>
60
+ <button (click)="modify()">modify item</button>
61
+ <button (click)="newLayout()">new layout</button>
62
+ <button (click)="saveGrid()">Save</button>
63
+ <button (click)="clearGrid()">Clear</button>
64
+ <button (click)="loadGrid()">Load</button>
65
+ <!-- TODO: addGrid() in code for testing instead ? -->
66
+ <gridstack [options]="nestedGridOptions" (changeCB)="onChange($event)" (resizeStopCB)="onResizeStop($event)">
67
+ <div empty-content>Add items here or reload the grid</div>
68
+ </gridstack>
69
+ </div>
70
+
71
+ <div class="grid-container"></div>
72
+
73
+ <div class="text-container">
74
+ <textarea #origTextArea cols="50" rows="50" readonly="readonly"></textarea>
75
+ <textarea #textArea cols="50" rows="50" readonly="readonly"></textarea>
76
+ </div>
77
+
78
+ </div>
@@ -0,0 +1,25 @@
1
+ import { TestBed } from '@angular/core/testing';
2
+ import { AppComponent } from './app.component';
3
+
4
+ describe('AppComponent', () => {
5
+ beforeEach(async () => {
6
+ await TestBed.configureTestingModule({
7
+ declarations: [
8
+ AppComponent
9
+ ],
10
+ }).compileComponents();
11
+ });
12
+
13
+ it('should create the app', () => {
14
+ const fixture = TestBed.createComponent(AppComponent);
15
+ const app = fixture.componentInstance;
16
+ expect(app).toBeTruthy();
17
+ });
18
+
19
+ it('should have content', () => {
20
+ const fixture = TestBed.createComponent(AppComponent);
21
+ fixture.detectChanges();
22
+ const compiled = fixture.nativeElement as HTMLElement;
23
+ expect(compiled.querySelector('.pick-info')?.textContent).toContain('Pick a demo to load');
24
+ });
25
+ });
@@ -0,0 +1,208 @@
1
+ import { Component, OnInit, ViewChild, ElementRef } from '@angular/core';
2
+ import { GridStack, GridStackOptions, GridStackWidget } from 'gridstack';
3
+ import { AngularSimpleComponent } from './simple';
4
+ import { AngularNgForTestComponent } from './ngFor';
5
+ import { AngularNgForCmdTestComponent } from './ngFor_cmd';
6
+
7
+ // NOTE: local testing of file
8
+ // import { GridstackComponent, NgGridStackOptions, NgGridStackWidget, elementCB, gsCreateNgComponents, nodesCB } from './gridstack.component';
9
+ import { GridstackComponent, NgGridStackOptions, NgGridStackWidget, elementCB, gsCreateNgComponents, nodesCB } from 'gridstack/dist/angular';
10
+
11
+ // unique ids sets for each item for correct ngFor updating
12
+ let ids = 1;
13
+ @Component({
14
+ selector: 'app-root',
15
+ templateUrl: './app.component.html',
16
+ styleUrls: ['./app.component.css']
17
+ })
18
+ export class AppComponent implements OnInit {
19
+
20
+ @ViewChild(AngularSimpleComponent) case0Comp?: AngularSimpleComponent;
21
+ @ViewChild(AngularNgForTestComponent) case1Comp?: AngularNgForTestComponent;
22
+ @ViewChild(AngularNgForCmdTestComponent) case2Comp?: AngularNgForCmdTestComponent;
23
+ @ViewChild(GridstackComponent) gridComp?: GridstackComponent;
24
+ @ViewChild('origTextArea', {static: true}) origTextEl?: ElementRef<HTMLTextAreaElement>;
25
+ @ViewChild('textArea', {static: true}) textEl?: ElementRef<HTMLTextAreaElement>;
26
+
27
+ // which sample to show
28
+ public show = 5;
29
+
30
+ /** sample grid options and items to load... */
31
+ public items: GridStackWidget[] = [
32
+ {x: 0, y: 0, minW: 2},
33
+ {x: 1, y: 1},
34
+ {x: 2, y: 2},
35
+ ];
36
+ public gridOptions: GridStackOptions = {
37
+ margin: 5,
38
+ float: true,
39
+ minRow: 1,
40
+ }
41
+ public gridOptionsFull: NgGridStackOptions = {
42
+ ...this.gridOptions,
43
+ children: [{x:0, y:0, selector:'app-a'}, {x:1, y:0, selector:'app-b'}, {x:2, y:0, content:'plain html'}],
44
+ }
45
+
46
+ // nested grid options
47
+ private subOptions: GridStackOptions = {
48
+ cellHeight: 50, // should be 50 - top/bottom
49
+ column: 'auto', // size to match container. make sure to include gridstack-extra.min.css
50
+ acceptWidgets: true, // will accept .grid-stack-item by default
51
+ margin: 5,
52
+ };
53
+ private 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}];
54
+ private sub2: NgGridStackWidget[] = [ {x:0, y:0}, {x:0, y:1, w:2}];
55
+ private subChildren: NgGridStackWidget[] = [
56
+ {x:0, y:0, content: 'regular item'},
57
+ {x:1, y:0, w:4, h:4, subGridOpts: {children: this.sub1, id:'sub1_grid', class: 'sub1', ...this.subOptions}},
58
+ {x:5, y:0, w:3, h:4, subGridOpts: {children: this.sub2, id:'sub2_grid', class: 'sub2', ...this.subOptions}},
59
+ ]
60
+ public nestedGridOptions: NgGridStackOptions = { // main grid options
61
+ cellHeight: 50,
62
+ margin: 5,
63
+ minRow: 2, // don't collapse when empty
64
+ disableOneColumnMode: true,
65
+ acceptWidgets: true,
66
+ id: 'main',
67
+ children: this.subChildren
68
+ };
69
+ private serializedData?: NgGridStackOptions;
70
+
71
+ constructor() {
72
+ // give them content and unique id to make sure we track them during changes below...
73
+ [...this.items, ...this.subChildren, ...this.sub1, ...this.sub2].forEach((w: NgGridStackWidget) => {
74
+ if (!w.selector && !w.subGridOpts) w.content = `item ${ids}`;
75
+ w.id = String(ids++);
76
+ });
77
+ }
78
+
79
+ ngOnInit(): void {
80
+ this.onShow(this.show);
81
+
82
+ // TEST
83
+ // setTimeout(() => {
84
+ // if (!this.gridComp) return;
85
+ // this.saveGrid();
86
+ // // this.clearGrid();
87
+ // this.delete();
88
+ // this.delete();
89
+ // this.loadGrid();
90
+ // this.delete();
91
+ // this.delete();
92
+ // }, 500)
93
+ }
94
+
95
+ public onShow(val: number) {
96
+ this.show = val;
97
+
98
+ // set globally our method to create the right widget type
99
+ if (val < 3) GridStack.addRemoveCB = undefined;
100
+ else GridStack.addRemoveCB = gsCreateNgComponents;
101
+
102
+ // let the switch take affect then load the starting values (since we sometimes save())
103
+ setTimeout(() => {
104
+ let data;
105
+ switch(val) {
106
+ case 0: data = this.case0Comp?.items; break;
107
+ case 1: data = this.case1Comp?.items; break;
108
+ case 2: data = this.case2Comp?.items; break;
109
+ case 3: data = this.gridComp?.grid?.save(true, true); break;
110
+ case 4: data = this.items; break;
111
+ case 5: data = this.gridOptionsFull; break;
112
+ case 6: data = this.nestedGridOptions; break;
113
+ }
114
+ if (this.origTextEl) this.origTextEl.nativeElement.value = JSON.stringify(data, null, ' ');
115
+ });
116
+ if (this.textEl) this.textEl.nativeElement.value = '';
117
+
118
+ // if (val === 6 && !this.gridComp) {
119
+ // const cont: HTMLElement | null = document.querySelector('.grid-container');
120
+ // if (cont) GridStack.addGrid(cont, this.serializedData);
121
+ // }
122
+ }
123
+
124
+ /** called whenever items change size/position/etc.. */
125
+ public onChange(data: nodesCB) {
126
+ // TODO: update our TEMPLATE list to match ?
127
+ // NOTE: no need for dynamic as we can always use grid.save() to get latest layout, or grid.engine.nodes
128
+ console.log('change ', data.nodes.length > 1 ? data.nodes : data.nodes[0]);
129
+ }
130
+
131
+ public onResizeStop(data: elementCB) {
132
+ console.log('resizestop ', data.el.gridstackNode);
133
+ }
134
+
135
+ /**
136
+ * TEST dynamic grid operations - uses grid API directly (since we don't track structure that gets out of sync)
137
+ */
138
+ public add() {
139
+ // TODO: BUG the content doesn't appear until widget is moved around (or another created). Need to force
140
+ // angular detection changes...
141
+ this.gridComp?.grid?.addWidget({x:3, y:0, w:2, content:`item ${ids}`, id:String(ids++)});
142
+ }
143
+ public delete() {
144
+ let grid = this.gridComp?.grid;
145
+ if (!grid) return;
146
+ let node = grid.engine.nodes[0];
147
+ // delete any children first before subGrid itself...
148
+ if (node?.subGrid && node.subGrid.engine.nodes.length) {
149
+ grid = node.subGrid;
150
+ node = grid.engine.nodes[0];
151
+ }
152
+ if (node) grid.removeWidget(node.el!);
153
+ }
154
+ public modify() {
155
+ this.gridComp?.grid?.update(this.gridComp?.grid.engine.nodes[0]?.el!, {w:3})
156
+ }
157
+ public newLayout() {
158
+ this.gridComp?.grid?.load([
159
+ {x:0, y:1, id:'1', minW:1, w:1}, // new size/constrain
160
+ {x:1, y:1, id:'2'},
161
+ // {x:2, y:1, id:'3'}, // delete item
162
+ {x:3, y:0, w:2, content:'new item'}, // new item
163
+ ]);
164
+ }
165
+
166
+ /**
167
+ * ngFor case: TEST TEMPLATE operations - NOT recommended unless you have no GS creating/re-parenting
168
+ */
169
+ public addNgFor() {
170
+ // new array isn't required as Angular detects changes to content with trackBy:identify()
171
+ // this.items = [...this.items, { x:3, y:0, w:3, content:`item ${ids}`, id:String(ids++) }];
172
+ this.items.push({x:3, y:0, w:2, content:`item ${ids}`, id:String(ids++)});
173
+ }
174
+ public deleteNgFor() {
175
+ this.items.pop();
176
+ }
177
+ public modifyNgFor() {
178
+ // this will not update the DOM nor trigger gridstackItems.changes for GS to auto-update, so set new option of the gridItem instead
179
+ // this.items[0].w = 3;
180
+ const gridItem = this.gridComp?.gridstackItems?.get(0);
181
+ if (gridItem) gridItem.options = {w:3};
182
+ }
183
+ public newLayoutNgFor() {
184
+ this.items = [
185
+ {x:0, y:1, id:'1', minW:1, w:1}, // new size/constrain
186
+ {x:1, y:1, id:'2'},
187
+ // {x:2, y:1, id:'3'}, // delete item
188
+ {x:3, y:0, w:2, content:'new item'}, // new item
189
+ ];
190
+ }
191
+ public clearGrid() {
192
+ if (!this.gridComp) return;
193
+ this.gridComp.grid?.removeAll(true);
194
+ }
195
+ public saveGrid() {
196
+ this.serializedData = this.gridComp?.grid?.save(false, true) as GridStackOptions || ''; // no content, full options
197
+ if (this.textEl) this.textEl.nativeElement.value = JSON.stringify(this.serializedData, null, ' ');
198
+ }
199
+ public loadGrid() {
200
+ if (!this.gridComp) return;
201
+ GridStack.addGrid(this.gridComp.el, this.serializedData);
202
+ }
203
+
204
+ // ngFor TEMPLATE unique node id to have correct match between our items used and GS
205
+ public identify(index: number, w: GridStackWidget) {
206
+ return w.id; // or use index if no id is set and you only modify at the end...
207
+ }
208
+ }
@@ -0,0 +1,39 @@
1
+ import { NgModule } from '@angular/core';
2
+ import { BrowserModule } from '@angular/platform-browser';
3
+
4
+ import { AppComponent } from './app.component';
5
+ import { AngularNgForTestComponent } from './ngFor';
6
+ import { AngularNgForCmdTestComponent } from './ngFor_cmd';
7
+ import { AngularSimpleComponent } from './simple';
8
+ import { AComponent, BComponent, CComponent } from './dummy.component';
9
+
10
+ // local testing
11
+ // import { GridstackModule } from './gridstack.module';
12
+ // import { GridstackComponent } from './gridstack.component';
13
+ import { GridstackModule, GridstackComponent } from 'gridstack/dist/angular';
14
+
15
+ @NgModule({
16
+ imports: [
17
+ BrowserModule,
18
+ GridstackModule,
19
+ ],
20
+ declarations: [
21
+ AngularNgForCmdTestComponent,
22
+ AngularNgForTestComponent,
23
+ AngularSimpleComponent,
24
+ AppComponent,
25
+ AComponent,
26
+ BComponent,
27
+ CComponent,
28
+ ],
29
+ exports: [
30
+ ],
31
+ providers: [],
32
+ bootstrap: [AppComponent]
33
+ })
34
+ export class AppModule {
35
+ constructor() {
36
+ // register all our dynamic components created in the grid
37
+ GridstackComponent.addComponentToSelectorType([AComponent, BComponent, CComponent]);
38
+ }
39
+ }
@@ -0,0 +1,35 @@
1
+ /**
2
+ * gridstack.component.ts 8.2.0
3
+ * Copyright (c) 2022 Alain Dumesny - see GridStack root license
4
+ */
5
+
6
+ // dummy testing component that will be grid items content
7
+
8
+ import { Component, OnDestroy, Input } from '@angular/core';
9
+ import { BaseWidget, NgCompInputs } from 'gridstack/dist/angular';
10
+
11
+ @Component({
12
+ selector: 'app-a',
13
+ template: 'Comp A {{text}}',
14
+ })
15
+ export class AComponent extends BaseWidget implements OnDestroy {
16
+ @Input() text: string = 'foo'; // test custom input data
17
+ public override serialize(): NgCompInputs | undefined { return this.text ? {text: this.text} : undefined; }
18
+ ngOnDestroy() {
19
+ console.log('Comp A destroyed'); // test to make sure cleanup happens
20
+ }
21
+ }
22
+
23
+ @Component({
24
+ selector: 'app-b',
25
+ template: 'Comp B',
26
+ })
27
+ export class BComponent extends BaseWidget {
28
+ }
29
+
30
+ @Component({
31
+ selector: 'app-c',
32
+ template: 'Comp C',
33
+ })
34
+ export class CComponent extends BaseWidget {
35
+ }
@@ -0,0 +1,131 @@
1
+ /**
2
+ * Example using Angular ngFor to loop through items and create DOM items
3
+ */
4
+
5
+ import { Component, AfterViewInit, Input, ViewChildren, QueryList, ElementRef } from '@angular/core';
6
+ import { GridItemHTMLElement, GridStack, GridStackNode, GridStackWidget, Utils } from 'gridstack';
7
+
8
+ // unique ids sets for each item for correct ngFor updating
9
+ let ids = 1;
10
+
11
+ @Component({
12
+ selector: "angular-ng-for-test",
13
+ template: `
14
+ <p><b>ngFor</b>: Example using Angular ngFor to loop through items and create DOM items. This track changes made to the array of items, waits for DOM rendering, then update GS</p>
15
+ <button (click)="add()">add item</button>
16
+ <button (click)="delete()">remove item</button>
17
+ <button (click)="modify()">modify item</button>
18
+ <button (click)="newLayout()">new layout</button>
19
+ <div class="grid-stack">
20
+ <!-- using angular templating to create DOM, otherwise an easier way is to simply call grid.load(items)
21
+ NOTE: this example is NOT complete as there are many more properties than listed (minW, maxW, etc....)
22
+ -->
23
+ <div *ngFor="let n of items; trackBy: identify"
24
+ class="grid-stack-item"
25
+ [attr.gs-id]="n.id"
26
+ [attr.gs-x]="n.x"
27
+ [attr.gs-y]="n.y"
28
+ [attr.gs-w]="n.w"
29
+ [attr.gs-h]="n.h"
30
+ #gridStackItem
31
+ >
32
+ <div class="grid-stack-item-content">item {{ n.id }}</div>
33
+ </div>
34
+ </div>
35
+ `,
36
+ // gridstack.min.css and other custom styles should be included in global styles.scss or here
37
+ })
38
+ export class AngularNgForTestComponent implements AfterViewInit {
39
+ /** list of HTML items that we track to know when the DOM has been updated to make/remove GS widgets */
40
+ @ViewChildren("gridStackItem") gridstackItems!: QueryList<ElementRef<GridItemHTMLElement>>;
41
+
42
+ /** set the items to display. */
43
+ @Input() public set items(list: GridStackWidget[]) {
44
+ this._items = list || [];
45
+ this._items.forEach(w => w.id = w.id || String(ids++)); // make sure a unique id is generated for correct ngFor loop update
46
+ }
47
+ public get items(): GridStackWidget[] { return this._items}
48
+
49
+ private grid!: GridStack;
50
+ public _items!: GridStackWidget[];
51
+
52
+ constructor() {
53
+ this.items = [
54
+ {x: 0, y: 0},
55
+ {x: 1, y: 1},
56
+ {x: 2, y: 2},
57
+ ];
58
+ }
59
+
60
+ // wait until after DOM is ready to init gridstack - can't be ngOnInit() as angular ngFor needs to run first!
61
+ public ngAfterViewInit() {
62
+ this.grid = GridStack.init({
63
+ margin: 5,
64
+ float: true,
65
+ })
66
+ .on('change added', (event: Event, nodes: GridStackNode[]) => this.onChange(nodes));
67
+
68
+ // sync initial actual valued rendered (in case init() had to merge conflicts)
69
+ this.onChange();
70
+
71
+ /**
72
+ * this is called when the list of items changes - get a list of nodes and
73
+ * update the layout accordingly (which will take care of adding/removing items changed by Angular)
74
+ */
75
+ this.gridstackItems.changes.subscribe(() => {
76
+ const layout: GridStackWidget[] = [];
77
+ this.gridstackItems.forEach(ref => {
78
+ const n = ref.nativeElement.gridstackNode || this.grid.makeWidget(ref.nativeElement).gridstackNode;
79
+ if (n) layout.push(n);
80
+ });
81
+ this.grid.load(layout); // efficient that does diffs only
82
+ })
83
+ }
84
+
85
+ /** Optional: called when given widgets are changed (moved/resized/added) - update our list to match.
86
+ * Note this is not strictly necessary as demo works without this
87
+ */
88
+ public onChange(list = this.grid.engine.nodes) {
89
+ setTimeout(() => // prevent new 'added' items from ExpressionChangedAfterItHasBeenCheckedError. TODO: find cleaner way to sync outside Angular change detection ?
90
+ list.forEach(n => {
91
+ const item = this._items.find(i => i.id === n.id);
92
+ if (item) Utils.copyPos(item, n);
93
+ })
94
+ , 0);
95
+ }
96
+
97
+ /**
98
+ * CRUD operations
99
+ */
100
+ public add() {
101
+ // new array isn't required as Angular seem to detect changes to content
102
+ // this.items = [...this.items, { x:3, y:0, w:3, id:String(ids++) }];
103
+ this.items.push({ x:3, y:0, w:3, id:String(ids++) });
104
+ }
105
+
106
+ public delete() {
107
+ this.items.pop();
108
+ }
109
+
110
+ public modify() {
111
+ // this will only update the DOM attr (from the ngFor loop in our template above)
112
+ // but not trigger gridstackItems.changes for GS to auto-update, so call GS update() instead
113
+ // this.items[0].w = 2;
114
+ const n = this.grid.engine.nodes[0];
115
+ if (n?.el) this.grid.update(n.el, {w:3});
116
+ }
117
+
118
+ public newLayout() {
119
+ this.items = [ // test updating existing and creating new one
120
+ {x:0, y:1, id:'1'},
121
+ {x:1, y:1, id:'2'},
122
+ // {x:2, y:1, id:3}, // delete item
123
+ {x:3, y:0, w:3}, // new item
124
+ ];
125
+ }
126
+
127
+ // ngFor unique node id to have correct match between our items used and GS
128
+ identify(index: number, w: GridStackWidget) {
129
+ return w.id;
130
+ }
131
+ }