Skip to content

Commit 21167d3

Browse files
Angular wrapper example improvement (#1824)
* Angular component wrapper
1 parent 3c1db44 commit 21167d3

8 files changed

+373
-0
lines changed

demo/angular/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,3 +25,4 @@ Run `ng e2e` to execute the end-to-end tests via a platform of your choice. To u
2525
## Further help
2626

2727
To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI Overview and Command Reference](https://angular.io/cli) page.
28+
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
<div class="grid-stack-item-content">
2+
<ng-content></ng-content>
3+
</div>
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
:host {
2+
display: block;
3+
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import {ChangeDetectionStrategy, Component, ElementRef, Input, NgZone, OnInit, Renderer2} from '@angular/core';
2+
3+
@Component({
4+
selector: 'ef-gridstack-item',
5+
templateUrl: './gridstack-item.component.html',
6+
styleUrls: ['./gridstack-item.component.scss'],
7+
changeDetection: ChangeDetectionStrategy.OnPush,
8+
})
9+
export class GridstackItemComponent implements OnInit {
10+
11+
@Input() public x: number;
12+
@Input() public y: number;
13+
@Input() public width: number;
14+
@Input() public height: number;
15+
@Input() public minW: number;
16+
@Input() public minH: number;
17+
@Input() public identifier: string;
18+
19+
constructor(
20+
private readonly ngZone: NgZone,
21+
private readonly _elementRef: ElementRef<HTMLElement>,
22+
private readonly renderer2: Renderer2,
23+
) {
24+
}
25+
26+
get elementRef(): ElementRef<HTMLElement> {
27+
return this._elementRef;
28+
}
29+
30+
public ngOnInit(): void {
31+
this.renderer2.addClass(this._elementRef.nativeElement, 'grid-stack-item');
32+
}
33+
34+
}

demo/angular/gridstack.component.html

Whitespace-only changes.

demo/angular/gridstack.component.scss

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
@use "sass:math";
2+
3+
$gridstack-max-columns: 24;
4+
5+
:host {
6+
display: block;
7+
}
8+
9+
::ng-deep {
10+
@mixin gen-grid-stack-item-width($gridstack-columns) {
11+
.grid-stack-#{$gridstack-columns} {
12+
.grid-stack-item {
13+
@for $i from 1 through $gridstack-columns {
14+
&[gs-w='#{$i}'] {
15+
width: math.div(100% , $gridstack-columns) * $i;
16+
}
17+
&[gs-x='#{$i}'] {
18+
left: math.div(100% , $gridstack-columns) * $i;
19+
}
20+
&[gs-min-w='#{$i}'] {
21+
min-width: math.div(100% , $gridstack-columns) * $i;
22+
}
23+
&[gs-max-w='#{$i}'] {
24+
max-width: math.div(100% ,$gridstack-columns) * $i;
25+
}
26+
}
27+
}
28+
}
29+
}
30+
@for $i from 1 through $gridstack-max-columns {
31+
@include gen-grid-stack-item-width($i);
32+
}
33+
}

demo/angular/gridstack.component.ts

Lines changed: 279 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,279 @@
1+
import {
2+
ChangeDetectionStrategy,
3+
Component,
4+
ContentChildren,
5+
ElementRef,
6+
EventEmitter,
7+
Input,
8+
NgZone,
9+
OnDestroy,
10+
OnInit,
11+
Output,
12+
QueryList,
13+
Renderer2,
14+
} from '@angular/core';
15+
import {GridstackItemComponent} from './gridstack-item.component';
16+
import {merge, Subject} from 'rxjs';
17+
import {GridStack, GridStackEvent, GridStackOptions} from 'gridstack';
18+
import 'gridstack/dist/h5/gridstack-dd-native';
19+
import {map, takeUntil} from 'rxjs/operators';
20+
21+
@Component({
22+
selector: 'ef-gridstack',
23+
templateUrl: './gridstack.component.html',
24+
styleUrls: ['./gridstack.component.scss'],
25+
changeDetection: ChangeDetectionStrategy.OnPush,
26+
})
27+
export class GridstackComponent implements OnInit, OnDestroy {
28+
29+
@ContentChildren(GridstackItemComponent) public gridstackItems: QueryList<GridstackItemComponent>;
30+
31+
@Input() public options: GridStackOptions;
32+
33+
@Output() public gridstackAdded = new EventEmitter<{
34+
event: GridStackEvent;
35+
grid: GridStack;
36+
}>();
37+
38+
@Output() public gridstackChange = new EventEmitter<{
39+
event: GridStackEvent;
40+
grid: GridStack;
41+
}>();
42+
43+
@Output() public gridstackDisable = new EventEmitter<{
44+
event: GridStackEvent;
45+
grid: GridStack;
46+
}>();
47+
48+
@Output() public gridstackDrag = new EventEmitter<{
49+
event: GridStackEvent;
50+
grid: GridStack;
51+
}>();
52+
53+
@Output() public gridstackDragStart = new EventEmitter<{
54+
event: GridStackEvent;
55+
grid: GridStack;
56+
}>();
57+
58+
@Output() public gridstackDragStop = new EventEmitter<{
59+
event: GridStackEvent;
60+
grid: GridStack;
61+
}>();
62+
63+
@Output() public gridstackDropped = new EventEmitter<{
64+
event: GridStackEvent;
65+
grid: GridStack;
66+
}>();
67+
68+
@Output() public gridstackEnable = new EventEmitter<{
69+
event: GridStackEvent;
70+
grid: GridStack;
71+
}>();
72+
73+
@Output() public gridstackRemoved = new EventEmitter<{
74+
event: GridStackEvent;
75+
grid: GridStack;
76+
}>();
77+
78+
@Output() public gridstackResize = new EventEmitter<{
79+
event: GridStackEvent;
80+
grid: GridStack;
81+
}>();
82+
83+
@Output() public gridstackResizeStart = new EventEmitter<{
84+
event: GridStackEvent;
85+
grid: GridStack;
86+
}>();
87+
88+
@Output() public gridstackResizeStop = new EventEmitter<{
89+
event: GridStackEvent;
90+
grid: GridStack;
91+
}>();
92+
93+
private readonly update$ = new Subject<void>();
94+
private readonly destroy$ = new Subject<boolean>();
95+
private _grid: GridStack;
96+
97+
constructor(
98+
private readonly ngZone: NgZone,
99+
private readonly elementRef: ElementRef<HTMLElement>,
100+
private readonly renderer2: Renderer2,
101+
) {
102+
}
103+
104+
get grid(): GridStack {
105+
return this._grid;
106+
}
107+
108+
public ngOnInit(): void {
109+
this.renderer2.addClass(this.elementRef.nativeElement, 'grid-stack');
110+
}
111+
112+
public ngAfterContentInit(): void {
113+
this.ngZone.runOutsideAngular(() => {
114+
this._grid = GridStack.init(this.options, this.elementRef.nativeElement);
115+
this.hookEvents(this._grid);
116+
merge(
117+
this.update$,
118+
this.gridstackItems.changes,
119+
).pipe(
120+
map(() => this.gridstackItems.toArray()),
121+
takeUntil(this.destroy$),
122+
).subscribe(items => {
123+
const gridItems = this._grid.getGridItems();
124+
let elementsToRemove = [...gridItems];
125+
this._grid.batchUpdate();
126+
this._grid.column(this.options.column);
127+
for (const item of items) {
128+
const existingItem = gridItems.find(x => x.gridstackNode.id === item.identifier);
129+
if (existingItem) {
130+
elementsToRemove = elementsToRemove.filter(x => x.gridstackNode.id !== item.identifier);
131+
this._grid.update(existingItem, {
132+
h: item.height,
133+
w: item.width,
134+
x: item.x,
135+
y: item.y,
136+
});
137+
} else {
138+
this._grid.addWidget(item.elementRef.nativeElement, {
139+
id: item.identifier,
140+
h: item.height,
141+
w: item.width,
142+
x: item.x,
143+
y: item.y,
144+
minW: item.minW,
145+
minH: item.minH,
146+
});
147+
}
148+
}
149+
for (const gridItemHTMLElement of elementsToRemove) {
150+
this._grid.removeWidget(gridItemHTMLElement);
151+
}
152+
this._grid.commit();
153+
});
154+
this.update$.next();
155+
});
156+
}
157+
158+
public ngOnDestroy(): void {
159+
this.destroy$.next(true);
160+
this.destroy$.complete();
161+
this.update$.complete();
162+
this._grid.destroy();
163+
}
164+
165+
update() {
166+
this.update$.next();
167+
}
168+
169+
private hookEvents(grid: GridStack) {
170+
grid.on('added', event => {
171+
this.ngZone.run(() => {
172+
this.gridstackAdded.emit({
173+
event: event as unknown as GridStackEvent,
174+
grid,
175+
});
176+
});
177+
});
178+
179+
grid.on('disable', event => {
180+
this.ngZone.run(() => {
181+
this.gridstackDisable.emit({
182+
event: event as unknown as GridStackEvent,
183+
grid,
184+
});
185+
});
186+
});
187+
188+
grid.on('enable', event => {
189+
this.ngZone.run(() => {
190+
this.gridstackEnable.emit({
191+
event: event as unknown as GridStackEvent,
192+
grid,
193+
});
194+
});
195+
});
196+
197+
grid.on('removed', event => {
198+
this.ngZone.run(() => {
199+
this.gridstackRemoved.emit({
200+
event: event as unknown as GridStackEvent,
201+
grid,
202+
});
203+
});
204+
});
205+
206+
grid.on('dropped', event => {
207+
this.ngZone.run(() => {
208+
this.gridstackDropped.emit({
209+
event: event as unknown as GridStackEvent,
210+
grid,
211+
});
212+
});
213+
});
214+
215+
grid.on('resize', event => {
216+
this.ngZone.run(() => {
217+
this.gridstackResize.emit({
218+
event: event as unknown as GridStackEvent,
219+
grid,
220+
});
221+
});
222+
});
223+
224+
grid.on('resizestart', event => {
225+
this.ngZone.run(() => {
226+
this.gridstackResizeStart.emit({
227+
event: event as unknown as GridStackEvent,
228+
grid,
229+
});
230+
});
231+
});
232+
233+
grid.on('resizestop', event => {
234+
this.ngZone.run(() => {
235+
this.gridstackResizeStop.emit({
236+
event: event as unknown as GridStackEvent,
237+
grid,
238+
});
239+
});
240+
});
241+
242+
grid.on('drag', event => {
243+
this.ngZone.run(() => {
244+
this.gridstackDrag.emit({
245+
event: event as unknown as GridStackEvent,
246+
grid,
247+
});
248+
});
249+
});
250+
251+
grid.on('dragstart', event => {
252+
this.ngZone.run(() => {
253+
this.gridstackDragStart.emit({
254+
event: event as unknown as GridStackEvent,
255+
grid,
256+
});
257+
});
258+
});
259+
260+
grid.on('dragstop', event => {
261+
this.ngZone.run(() => {
262+
this.gridstackDragStop.emit({
263+
event: event as unknown as GridStackEvent,
264+
grid,
265+
});
266+
});
267+
});
268+
269+
grid.on('change', event => {
270+
this.ngZone.run(() => {
271+
this.gridstackChange.emit({
272+
event: event as unknown as GridStackEvent,
273+
grid,
274+
});
275+
});
276+
});
277+
}
278+
279+
}

demo/angular/gridstack.module.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import {NgModule} from '@angular/core';
2+
import {CommonModule} from '@angular/common';
3+
import {GridstackComponent} from './gridstack.component';
4+
import {GridstackItemComponent} from './gridstack-item.component';
5+
6+
@NgModule({
7+
declarations: [
8+
GridstackComponent,
9+
GridstackItemComponent,
10+
],
11+
exports: [
12+
GridstackComponent,
13+
GridstackItemComponent,
14+
],
15+
imports: [
16+
CommonModule,
17+
],
18+
})
19+
export class GridstackModule {
20+
}

0 commit comments

Comments
 (0)