From 21167d3b87165b9e88ff8831310bb033c9d72a6e Mon Sep 17 00:00:00 2001 From: David Lacroix Date: Wed, 28 Dec 2022 14:29:11 -0500 Subject: [PATCH] Angular wrapper example improvement (#1824) * Angular component wrapper --- demo/angular/README.md | 1 + demo/angular/gridstack-item.component.html | 3 + demo/angular/gridstack-item.component.scss | 3 + demo/angular/gridstack-item.component.ts | 34 +++ demo/angular/gridstack.component.html | 0 demo/angular/gridstack.component.scss | 33 +++ demo/angular/gridstack.component.ts | 279 +++++++++++++++++++++ demo/angular/gridstack.module.ts | 20 ++ 8 files changed, 373 insertions(+) create mode 100644 demo/angular/gridstack-item.component.html create mode 100644 demo/angular/gridstack-item.component.scss create mode 100644 demo/angular/gridstack-item.component.ts create mode 100644 demo/angular/gridstack.component.html create mode 100644 demo/angular/gridstack.component.scss create mode 100644 demo/angular/gridstack.component.ts create mode 100644 demo/angular/gridstack.module.ts diff --git a/demo/angular/README.md b/demo/angular/README.md index ce79769a5..112b3ddd8 100644 --- a/demo/angular/README.md +++ b/demo/angular/README.md @@ -25,3 +25,4 @@ Run `ng e2e` to execute the end-to-end tests via a platform of your choice. To u ## Further help 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. + diff --git a/demo/angular/gridstack-item.component.html b/demo/angular/gridstack-item.component.html new file mode 100644 index 000000000..01ccdc0bf --- /dev/null +++ b/demo/angular/gridstack-item.component.html @@ -0,0 +1,3 @@ +
+ +
diff --git a/demo/angular/gridstack-item.component.scss b/demo/angular/gridstack-item.component.scss new file mode 100644 index 000000000..5d4e87f30 --- /dev/null +++ b/demo/angular/gridstack-item.component.scss @@ -0,0 +1,3 @@ +:host { + display: block; +} diff --git a/demo/angular/gridstack-item.component.ts b/demo/angular/gridstack-item.component.ts new file mode 100644 index 000000000..9c1d17191 --- /dev/null +++ b/demo/angular/gridstack-item.component.ts @@ -0,0 +1,34 @@ +import {ChangeDetectionStrategy, Component, ElementRef, Input, NgZone, OnInit, Renderer2} from '@angular/core'; + +@Component({ + selector: 'ef-gridstack-item', + templateUrl: './gridstack-item.component.html', + styleUrls: ['./gridstack-item.component.scss'], + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class GridstackItemComponent implements OnInit { + + @Input() public x: number; + @Input() public y: number; + @Input() public width: number; + @Input() public height: number; + @Input() public minW: number; + @Input() public minH: number; + @Input() public identifier: string; + + constructor( + private readonly ngZone: NgZone, + private readonly _elementRef: ElementRef, + private readonly renderer2: Renderer2, + ) { + } + + get elementRef(): ElementRef { + return this._elementRef; + } + + public ngOnInit(): void { + this.renderer2.addClass(this._elementRef.nativeElement, 'grid-stack-item'); + } + +} diff --git a/demo/angular/gridstack.component.html b/demo/angular/gridstack.component.html new file mode 100644 index 000000000..e69de29bb diff --git a/demo/angular/gridstack.component.scss b/demo/angular/gridstack.component.scss new file mode 100644 index 000000000..249bac810 --- /dev/null +++ b/demo/angular/gridstack.component.scss @@ -0,0 +1,33 @@ +@use "sass:math"; + +$gridstack-max-columns: 24; + +:host { + display: block; +} + +::ng-deep { + @mixin gen-grid-stack-item-width($gridstack-columns) { + .grid-stack-#{$gridstack-columns} { + .grid-stack-item { + @for $i from 1 through $gridstack-columns { + &[gs-w='#{$i}'] { + width: math.div(100% , $gridstack-columns) * $i; + } + &[gs-x='#{$i}'] { + left: math.div(100% , $gridstack-columns) * $i; + } + &[gs-min-w='#{$i}'] { + min-width: math.div(100% , $gridstack-columns) * $i; + } + &[gs-max-w='#{$i}'] { + max-width: math.div(100% ,$gridstack-columns) * $i; + } + } + } + } + } + @for $i from 1 through $gridstack-max-columns { + @include gen-grid-stack-item-width($i); + } +} diff --git a/demo/angular/gridstack.component.ts b/demo/angular/gridstack.component.ts new file mode 100644 index 000000000..2f69f0cc7 --- /dev/null +++ b/demo/angular/gridstack.component.ts @@ -0,0 +1,279 @@ +import { + ChangeDetectionStrategy, + Component, + ContentChildren, + ElementRef, + EventEmitter, + Input, + NgZone, + OnDestroy, + OnInit, + Output, + QueryList, + Renderer2, +} from '@angular/core'; +import {GridstackItemComponent} from './gridstack-item.component'; +import {merge, Subject} from 'rxjs'; +import {GridStack, GridStackEvent, GridStackOptions} from 'gridstack'; +import 'gridstack/dist/h5/gridstack-dd-native'; +import {map, takeUntil} from 'rxjs/operators'; + +@Component({ + selector: 'ef-gridstack', + templateUrl: './gridstack.component.html', + styleUrls: ['./gridstack.component.scss'], + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class GridstackComponent implements OnInit, OnDestroy { + + @ContentChildren(GridstackItemComponent) public gridstackItems: QueryList; + + @Input() public options: GridStackOptions; + + @Output() public gridstackAdded = new EventEmitter<{ + event: GridStackEvent; + grid: GridStack; + }>(); + + @Output() public gridstackChange = new EventEmitter<{ + event: GridStackEvent; + grid: GridStack; + }>(); + + @Output() public gridstackDisable = new EventEmitter<{ + event: GridStackEvent; + grid: GridStack; + }>(); + + @Output() public gridstackDrag = new EventEmitter<{ + event: GridStackEvent; + grid: GridStack; + }>(); + + @Output() public gridstackDragStart = new EventEmitter<{ + event: GridStackEvent; + grid: GridStack; + }>(); + + @Output() public gridstackDragStop = new EventEmitter<{ + event: GridStackEvent; + grid: GridStack; + }>(); + + @Output() public gridstackDropped = new EventEmitter<{ + event: GridStackEvent; + grid: GridStack; + }>(); + + @Output() public gridstackEnable = new EventEmitter<{ + event: GridStackEvent; + grid: GridStack; + }>(); + + @Output() public gridstackRemoved = new EventEmitter<{ + event: GridStackEvent; + grid: GridStack; + }>(); + + @Output() public gridstackResize = new EventEmitter<{ + event: GridStackEvent; + grid: GridStack; + }>(); + + @Output() public gridstackResizeStart = new EventEmitter<{ + event: GridStackEvent; + grid: GridStack; + }>(); + + @Output() public gridstackResizeStop = new EventEmitter<{ + event: GridStackEvent; + grid: GridStack; + }>(); + + private readonly update$ = new Subject(); + private readonly destroy$ = new Subject(); + private _grid: GridStack; + + constructor( + private readonly ngZone: NgZone, + private readonly elementRef: ElementRef, + private readonly renderer2: Renderer2, + ) { + } + + get grid(): GridStack { + return this._grid; + } + + public ngOnInit(): void { + this.renderer2.addClass(this.elementRef.nativeElement, 'grid-stack'); + } + + public ngAfterContentInit(): void { + this.ngZone.runOutsideAngular(() => { + this._grid = GridStack.init(this.options, this.elementRef.nativeElement); + this.hookEvents(this._grid); + merge( + this.update$, + this.gridstackItems.changes, + ).pipe( + map(() => this.gridstackItems.toArray()), + takeUntil(this.destroy$), + ).subscribe(items => { + const gridItems = this._grid.getGridItems(); + let elementsToRemove = [...gridItems]; + this._grid.batchUpdate(); + this._grid.column(this.options.column); + for (const item of items) { + const existingItem = gridItems.find(x => x.gridstackNode.id === item.identifier); + if (existingItem) { + elementsToRemove = elementsToRemove.filter(x => x.gridstackNode.id !== item.identifier); + this._grid.update(existingItem, { + h: item.height, + w: item.width, + x: item.x, + y: item.y, + }); + } else { + this._grid.addWidget(item.elementRef.nativeElement, { + id: item.identifier, + h: item.height, + w: item.width, + x: item.x, + y: item.y, + minW: item.minW, + minH: item.minH, + }); + } + } + for (const gridItemHTMLElement of elementsToRemove) { + this._grid.removeWidget(gridItemHTMLElement); + } + this._grid.commit(); + }); + this.update$.next(); + }); + } + + public ngOnDestroy(): void { + this.destroy$.next(true); + this.destroy$.complete(); + this.update$.complete(); + this._grid.destroy(); + } + + update() { + this.update$.next(); + } + + private hookEvents(grid: GridStack) { + grid.on('added', event => { + this.ngZone.run(() => { + this.gridstackAdded.emit({ + event: event as unknown as GridStackEvent, + grid, + }); + }); + }); + + grid.on('disable', event => { + this.ngZone.run(() => { + this.gridstackDisable.emit({ + event: event as unknown as GridStackEvent, + grid, + }); + }); + }); + + grid.on('enable', event => { + this.ngZone.run(() => { + this.gridstackEnable.emit({ + event: event as unknown as GridStackEvent, + grid, + }); + }); + }); + + grid.on('removed', event => { + this.ngZone.run(() => { + this.gridstackRemoved.emit({ + event: event as unknown as GridStackEvent, + grid, + }); + }); + }); + + grid.on('dropped', event => { + this.ngZone.run(() => { + this.gridstackDropped.emit({ + event: event as unknown as GridStackEvent, + grid, + }); + }); + }); + + grid.on('resize', event => { + this.ngZone.run(() => { + this.gridstackResize.emit({ + event: event as unknown as GridStackEvent, + grid, + }); + }); + }); + + grid.on('resizestart', event => { + this.ngZone.run(() => { + this.gridstackResizeStart.emit({ + event: event as unknown as GridStackEvent, + grid, + }); + }); + }); + + grid.on('resizestop', event => { + this.ngZone.run(() => { + this.gridstackResizeStop.emit({ + event: event as unknown as GridStackEvent, + grid, + }); + }); + }); + + grid.on('drag', event => { + this.ngZone.run(() => { + this.gridstackDrag.emit({ + event: event as unknown as GridStackEvent, + grid, + }); + }); + }); + + grid.on('dragstart', event => { + this.ngZone.run(() => { + this.gridstackDragStart.emit({ + event: event as unknown as GridStackEvent, + grid, + }); + }); + }); + + grid.on('dragstop', event => { + this.ngZone.run(() => { + this.gridstackDragStop.emit({ + event: event as unknown as GridStackEvent, + grid, + }); + }); + }); + + grid.on('change', event => { + this.ngZone.run(() => { + this.gridstackChange.emit({ + event: event as unknown as GridStackEvent, + grid, + }); + }); + }); + } + +} diff --git a/demo/angular/gridstack.module.ts b/demo/angular/gridstack.module.ts new file mode 100644 index 000000000..44fd880d4 --- /dev/null +++ b/demo/angular/gridstack.module.ts @@ -0,0 +1,20 @@ +import {NgModule} from '@angular/core'; +import {CommonModule} from '@angular/common'; +import {GridstackComponent} from './gridstack.component'; +import {GridstackItemComponent} from './gridstack-item.component'; + +@NgModule({ + declarations: [ + GridstackComponent, + GridstackItemComponent, + ], + exports: [ + GridstackComponent, + GridstackItemComponent, + ], + imports: [ + CommonModule, + ], +}) +export class GridstackModule { +}