N°8641 - Dashboard editor front-end first commit for Form SDK integration.

* No dashlet edition
* Dashboard are not persisted
* Unable to load a dashboard from an endpoint (refresh)
* Grid library need proper npm integration
This commit is contained in:
Stephen Abello
2026-01-06 15:23:51 +01:00
parent 3e879c64a7
commit a713e1b56e
167 changed files with 32266 additions and 763 deletions

211
node_modules/gridstack/dist/angular/README.md generated vendored Normal file
View File

@@ -0,0 +1,211 @@
# Angular wrapper
The Angular [wrapper component](projects/lib/src/lib/gridstack.component.ts) <gridstack> is a <b>better way to use Gridstack</b>, but alternative raw [ngFor](projects/demo/src/app/ngFor.ts) or [simple](projects/demo/src/app/simple.ts) demos are also given.
Running version can be seen here https://stackblitz.com/edit/gridstack-angular
# Dynamic grid items
this is the recommended way if you are going to have multiple grids (alow drag&drop between) or drag from toolbar to create items, or drag to remove items, etc...
I.E. don't use Angular templating to create grid items as that is harder to sync when gridstack will also add/remove items.
MyComponent HTML
```html
<gridstack [options]="gridOptions"></gridstack>
```
MyComponent CSS
```css
@import "gridstack/dist/gridstack.min.css";
.grid-stack {
background: #fafad2;
}
.grid-stack-item-content {
text-align: center;
background-color: #18bc9c;
}
```
Standalone MyComponent Code
```ts
import { GridStackOptions } from 'gridstack';
import { GridstackComponent, GridstackItemComponent } from 'gridstack/dist/angular';
@Component({
imports: [ // SKIP if doing module import instead (next)
GridstackComponent,
GridstackItemComponent
]
...
})
export class MyComponent {
// sample grid options + items to load...
public gridOptions: GridStackOptions = {
margin: 5,
children: [ // or call load(children) or addWidget(children[0]) with same data
{x:0, y:0, minW:2, content:'Item 1'},
{x:1, y:0, content:'Item 2'},
{x:0, y:1, content:'Item 3'},
]
}
}
```
IF doing module import instead of standalone, you will also need this:
```ts
import { GridstackModule } from 'gridstack/dist/angular';
@NgModule({
imports: [GridstackModule, ...]
...
bootstrap: [AppComponent]
})
export class AppModule { }
```
# More Complete example
In this example (build on previous one) will use your actual custom angular components inside each grid item (instead of dummy html content) and have per component saved settings as well (using BaseWidget).
HTML
```html
<gridstack [options]="gridOptions" (changeCB)="onChange($event)">
<div empty-content>message when grid is empty</div>
</gridstack>
```
Code
```ts
import { Component } from '@angular/core';
import { GridStack, GridStackOptions } from 'gridstack';
import { GridstackComponent, gsCreateNgComponents, NgGridStackWidget, nodesCB, BaseWidget } from 'gridstack/dist/angular';
// some custom components
@Component({
selector: 'app-a',
template: 'Comp A {{text}}',
})
export class AComponent extends BaseWidget implements OnDestroy {
@Input() text: string = 'foo'; // test custom input data
public override serialize(): NgCompInputs | undefined { return this.text ? {text: this.text} : undefined; }
ngOnDestroy() {
console.log('Comp A destroyed'); // test to make sure cleanup happens
}
}
@Component({
selector: 'app-b',
template: 'Comp B',
})
export class BComponent extends BaseWidget {
}
// ...in your module (classic), OR your ng19 app.config provideEnvironmentInitializer call this:
constructor() {
// register all our dynamic components types created by the grid
GridstackComponent.addComponentToSelectorType([AComponent, BComponent]) ;
}
// now our content will use Components instead of dummy html content
public gridOptions: NgGridStackOptions = {
margin: 5,
minRow: 1, // make space for empty message
children: [ // or call load()/addWidget() with same data
{x:0, y:0, minW:2, selector:'app-a'},
{x:1, y:0, minW:2, selector:'app-a', input: { text: 'bar' }}, // custom input that works using BaseWidget.deserialize() Object.assign(this, w.input)
{x:2, y:0, selector:'app-b'},
{x:3, y:0, content:'plain html'},
]
}
// called whenever items change size/position/etc.. see other events
public onChange(data: nodesCB) {
console.log('change ', data.nodes.length > 1 ? data.nodes : data.nodes[0]);
}
```
# ngFor with wrapper
For simple case where you control the children creation (gridstack doesn't do create or re-parenting)
HTML
```html
<gridstack [options]="gridOptions" (changeCB)="onChange($event)">
<!-- Angular 17+ -->
@for (n of items; track n.id) {
<gridstack-item [options]="n">Item {{n.id}}</gridstack-item>
}
<!-- Angular 16 -->
<gridstack-item *ngFor="let n of items; trackBy: identify" [options]="n"> Item {{n.id}} </gridstack-item>
</gridstack>
```
Code
```javascript
import { GridStackOptions, GridStackWidget } from 'gridstack';
import { nodesCB } from 'gridstack/dist/angular';
/** sample grid options and items to load... */
public gridOptions: GridStackOptions = { margin: 5 }
public items: GridStackWidget[] = [
{x:0, y:0, minW:2, id:'1'}, // must have unique id used for trackBy
{x:1, y:0, id:'2'},
{x:0, y:1, id:'3'},
];
// called whenever items change size/position/etc..
public onChange(data: nodesCB) {
console.log('change ', data.nodes.length > 1 ? data.nodes : data.nodes[0]);
}
// ngFor unique node id to have correct match between our items used and GS
public identify(index: number, w: GridStackWidget) {
return w.id; // or use index if no id is set and you only modify at the end...
}
```
## Demo
You can see a fuller example at [app.component.ts](projects/demo/src/app/app.component.ts)
to build the demo, go to [angular/projects/demo](projects/demo/) and run `yarn` + `yarn start` and navigate to `http://localhost:4200/`
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`)
## Caveats
- This wrapper needs:
- gridstack v8+ to run as it needs the latest changes (use older version that matches GS versions)
- <b>Angular 14+</b> for dynamic `createComponent()` API and Standalone Components (verified against 19+)
NOTE: if you are on Angular 13 or below: copy the wrapper code over (or patch it - see main page example) and change `createComponent()` calls to use old API instead:
NOTE2: now that we're using standalone, you will also need to remove `standalone: true` and `imports` on each component so you will to copy those locally (or use <11.1.2 version)
```ts
protected resolver: ComponentFactoryResolver,
...
const factory = this.resolver.resolveComponentFactory(GridItemComponent);
const gridItemRef = grid.container.createComponent(factory) as ComponentRef<GridItemComponent>;
// ...do the same for widget selector...
```
## ngFor Caveats
- This wrapper handles well ngFor loops, but if you're using a trackBy function (as I would recommend) and no element id change after an update,
you must manually update the `GridstackItemComponent.option` directly - see [modifyNgFor()](./projects/demo/src/app/app.component.ts#L202) example.
- The original client list of items is not updated to match **content** changes made by gridstack (TBD later), but adding new item or removing (as shown in demo) will update those new items. Client could use change/added/removed events to sync that list if they wish to do so.
Would appreciate getting help doing the same for React and Vue (2 other popular frameworks)
-Alain

View File

@@ -0,0 +1,5 @@
/**
* Generated bundle index. Do not edit.
*/
export * from './index';
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZ3JpZHN0YWNrLWFuZ3VsYXIuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi9hbmd1bGFyL3Byb2plY3RzL2xpYi9zcmMvZ3JpZHN0YWNrLWFuZ3VsYXIudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7O0dBRUc7QUFFSCxjQUFjLFNBQVMsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbIi8qKlxuICogR2VuZXJhdGVkIGJ1bmRsZSBpbmRleC4gRG8gbm90IGVkaXQuXG4gKi9cblxuZXhwb3J0ICogZnJvbSAnLi9pbmRleCc7XG4iXX0=

View File

@@ -0,0 +1,9 @@
/*
* Public API Surface of gridstack-angular
*/
export * from './lib/types';
export * from './lib/base-widget';
export * from './lib/gridstack-item.component';
export * from './lib/gridstack.component';
export * from './lib/gridstack.module';
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi9hbmd1bGFyL3Byb2plY3RzL2xpYi9zcmMvaW5kZXgudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7O0dBRUc7QUFFSCxjQUFjLGFBQWEsQ0FBQztBQUM1QixjQUFjLG1CQUFtQixDQUFDO0FBQ2xDLGNBQWMsZ0NBQWdDLENBQUM7QUFDL0MsY0FBYywyQkFBMkIsQ0FBQztBQUMxQyxjQUFjLHdCQUF3QixDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiLypcbiAqIFB1YmxpYyBBUEkgU3VyZmFjZSBvZiBncmlkc3RhY2stYW5ndWxhclxuICovXG5cbmV4cG9ydCAqIGZyb20gJy4vbGliL3R5cGVzJztcbmV4cG9ydCAqIGZyb20gJy4vbGliL2Jhc2Utd2lkZ2V0JztcbmV4cG9ydCAqIGZyb20gJy4vbGliL2dyaWRzdGFjay1pdGVtLmNvbXBvbmVudCc7XG5leHBvcnQgKiBmcm9tICcuL2xpYi9ncmlkc3RhY2suY29tcG9uZW50JztcbmV4cG9ydCAqIGZyb20gJy4vbGliL2dyaWRzdGFjay5tb2R1bGUnO1xuIl19

View File

@@ -0,0 +1,90 @@
/**
* gridstack-item.component.ts 12.3.3
* Copyright (c) 2022-2024 Alain Dumesny - see GridStack root license
*/
/**
* Abstract base class that all custom widgets should extend.
*
* This class provides the interface needed for GridstackItemComponent to:
* - Serialize/deserialize widget data
* - Save/restore widget state
* - Integrate with Angular lifecycle
*
* Extend this class when creating custom widgets for dynamic grids.
*
* @example
* ```typescript
* @Component({
* selector: 'my-custom-widget',
* template: '<div>{{data}}</div>'
* })
* export class MyCustomWidget extends BaseWidget {
* @Input() data: string = '';
*
* serialize() {
* return { data: this.data };
* }
* }
* ```
*/
import { Injectable } from '@angular/core';
import * as i0 from "@angular/core";
/**
* Base widget class for GridStack Angular integration.
*/
export class BaseWidget {
/**
* Override this method to return serializable data for this widget.
*
* Return an object with properties that map to your component's @Input() fields.
* The selector is handled automatically, so only include component-specific data.
*
* @returns Object containing serializable component data
*
* @example
* ```typescript
* serialize() {
* return {
* title: this.title,
* value: this.value,
* settings: this.settings
* };
* }
* ```
*/
serialize() { return; }
/**
* Override this method to handle widget restoration from saved data.
*
* Use this for complex initialization that goes beyond simple @Input() mapping.
* The default implementation automatically assigns input data to component properties.
*
* @param w The saved widget data including input properties
*
* @example
* ```typescript
* deserialize(w: NgGridStackWidget) {
* super.deserialize(w); // Call parent for basic setup
*
* // Custom initialization logic
* if (w.input?.complexData) {
* this.processComplexData(w.input.complexData);
* }
* }
* ```
*/
deserialize(w) {
// save full description for meta data
this.widgetItem = w;
if (!w)
return;
if (w.input)
Object.assign(this, w.input);
}
}
BaseWidget.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: BaseWidget, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
BaseWidget.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: BaseWidget });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: BaseWidget, decorators: [{
type: Injectable
}] });
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYmFzZS13aWRnZXQuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi9hbmd1bGFyL3Byb2plY3RzL2xpYi9zcmMvbGliL2Jhc2Utd2lkZ2V0LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBOzs7R0FHRztBQUVIOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7R0F3Qkc7QUFFSCxPQUFPLEVBQUUsVUFBVSxFQUFFLE1BQU0sZUFBZSxDQUFDOztBQUczQzs7R0FFRztBQUVILE1BQU0sT0FBZ0IsVUFBVTtJQVE5Qjs7Ozs7Ozs7Ozs7Ozs7Ozs7O09Ba0JHO0lBQ0ksU0FBUyxLQUFnQyxPQUFPLENBQUMsQ0FBQztJQUV6RDs7Ozs7Ozs7Ozs7Ozs7Ozs7OztPQW1CRztJQUNJLFdBQVcsQ0FBQyxDQUFvQjtRQUNyQyxzQ0FBc0M7UUFDdEMsSUFBSSxDQUFDLFVBQVUsR0FBRyxDQUFDLENBQUM7UUFDcEIsSUFBSSxDQUFDLENBQUM7WUFBRSxPQUFPO1FBRWYsSUFBSSxDQUFDLENBQUMsS0FBSztZQUFHLE1BQU0sQ0FBQyxNQUFNLENBQUMsSUFBSSxFQUFFLENBQUMsQ0FBQyxLQUFLLENBQUMsQ0FBQztJQUM3QyxDQUFDOzt1R0F2RG1CLFVBQVU7MkdBQVYsVUFBVTsyRkFBVixVQUFVO2tCQUQvQixVQUFVIiwic291cmNlc0NvbnRlbnQiOlsiLyoqXHJcbiAqIGdyaWRzdGFjay1pdGVtLmNvbXBvbmVudC50cyAxMi4zLjNcclxuICogQ29weXJpZ2h0IChjKSAyMDIyLTIwMjQgQWxhaW4gRHVtZXNueSAtIHNlZSBHcmlkU3RhY2sgcm9vdCBsaWNlbnNlXHJcbiAqL1xyXG5cclxuLyoqXHJcbiAqIEFic3RyYWN0IGJhc2UgY2xhc3MgdGhhdCBhbGwgY3VzdG9tIHdpZGdldHMgc2hvdWxkIGV4dGVuZC5cclxuICogXHJcbiAqIFRoaXMgY2xhc3MgcHJvdmlkZXMgdGhlIGludGVyZmFjZSBuZWVkZWQgZm9yIEdyaWRzdGFja0l0ZW1Db21wb25lbnQgdG86XHJcbiAqIC0gU2VyaWFsaXplL2Rlc2VyaWFsaXplIHdpZGdldCBkYXRhXHJcbiAqIC0gU2F2ZS9yZXN0b3JlIHdpZGdldCBzdGF0ZVxyXG4gKiAtIEludGVncmF0ZSB3aXRoIEFuZ3VsYXIgbGlmZWN5Y2xlXHJcbiAqIFxyXG4gKiBFeHRlbmQgdGhpcyBjbGFzcyB3aGVuIGNyZWF0aW5nIGN1c3RvbSB3aWRnZXRzIGZvciBkeW5hbWljIGdyaWRzLlxyXG4gKiBcclxuICogQGV4YW1wbGVcclxuICogYGBgdHlwZXNjcmlwdFxyXG4gKiBAQ29tcG9uZW50KHtcclxuICogICBzZWxlY3RvcjogJ215LWN1c3RvbS13aWRnZXQnLFxyXG4gKiAgIHRlbXBsYXRlOiAnPGRpdj57e2RhdGF9fTwvZGl2PidcclxuICogfSlcclxuICogZXhwb3J0IGNsYXNzIE15Q3VzdG9tV2lkZ2V0IGV4dGVuZHMgQmFzZVdpZGdldCB7XHJcbiAqICAgQElucHV0KCkgZGF0YTogc3RyaW5nID0gJyc7XHJcbiAqICAgXHJcbiAqICAgc2VyaWFsaXplKCkge1xyXG4gKiAgICAgcmV0dXJuIHsgZGF0YTogdGhpcy5kYXRhIH07XHJcbiAqICAgfVxyXG4gKiB9XHJcbiAqIGBgYFxyXG4gKi9cclxuXHJcbmltcG9ydCB7IEluamVjdGFibGUgfSBmcm9tICdAYW5ndWxhci9jb3JlJztcclxuaW1wb3J0IHsgTmdDb21wSW5wdXRzLCBOZ0dyaWRTdGFja1dpZGdldCB9IGZyb20gJy4vdHlwZXMnO1xyXG5cclxuLyoqXHJcbiAqIEJhc2Ugd2lkZ2V0IGNsYXNzIGZvciBHcmlkU3RhY2sgQW5ndWxhciBpbnRlZ3JhdGlvbi5cclxuICovXHJcbkBJbmplY3RhYmxlKClcclxuZXhwb3J0IGFic3RyYWN0IGNsYXNzIEJhc2VXaWRnZXQge1xyXG5cclxuICAvKipcclxuICAgKiBDb21wbGV0ZSB3aWRnZXQgZGVmaW5pdGlvbiBpbmNsdWRpbmcgcG9zaXRpb24sIHNpemUsIGFuZCBBbmd1bGFyLXNwZWNpZmljIGRhdGEuXHJcbiAgICogUG9wdWxhdGVkIGF1dG9tYXRpY2FsbHkgd2hlbiB0aGUgd2lkZ2V0IGlzIGxvYWRlZCBvciBzYXZlZC5cclxuICAgKi9cclxuICBwdWJsaWMgd2lkZ2V0SXRlbT86IE5nR3JpZFN0YWNrV2lkZ2V0O1xyXG5cclxuICAvKipcclxuICAgKiBPdmVycmlkZSB0aGlzIG1ldGhvZCB0byByZXR1cm4gc2VyaWFsaXphYmxlIGRhdGEgZm9yIHRoaXMgd2lkZ2V0LlxyXG4gICAqIFxyXG4gICAqIFJldHVybiBhbiBvYmplY3Qgd2l0aCBwcm9wZXJ0aWVzIHRoYXQgbWFwIHRvIHlvdXIgY29tcG9uZW50J3MgQElucHV0KCkgZmllbGRzLlxyXG4gICAqIFRoZSBzZWxlY3RvciBpcyBoYW5kbGVkIGF1dG9tYXRpY2FsbHksIHNvIG9ubHkgaW5jbHVkZSBjb21wb25lbnQtc3BlY2lmaWMgZGF0YS5cclxuICAgKiBcclxuICAgKiBAcmV0dXJucyBPYmplY3QgY29udGFpbmluZyBzZXJpYWxpemFibGUgY29tcG9uZW50IGRhdGFcclxuICAgKiBcclxuICAgKiBAZXhhbXBsZVxyXG4gICAqIGBgYHR5cGVzY3JpcHRcclxuICAgKiBzZXJpYWxpemUoKSB7XHJcbiAgICogICByZXR1cm4ge1xyXG4gICAqICAgICB0aXRsZTogdGhpcy50aXRsZSxcclxuICAgKiAgICAgdmFsdWU6IHRoaXMudmFsdWUsXHJcbiAgICogICAgIHNldHRpbmdzOiB0aGlzLnNldHRpbmdzXHJcbiAgICogICB9O1xyXG4gICAqIH1cclxuICAgKiBgYGBcclxuICAgKi9cclxuICBwdWJsaWMgc2VyaWFsaXplKCk6IE5nQ29tcElucHV0cyB8IHVuZGVmaW5lZCAgeyByZXR1cm47IH1cclxuXHJcbiAgLyoqXHJcbiAgICogT3ZlcnJpZGUgdGhpcyBtZXRob2QgdG8gaGFuZGxlIHdpZGdldCByZXN0b3JhdGlvbiBmcm9tIHNhdmVkIGRhdGEuXHJcbiAgICogXHJcbiAgICogVXNlIHRoaXMgZm9yIGNvbXBsZXggaW5pdGlhbGl6YXRpb24gdGhhdCBnb2VzIGJleW9uZCBzaW1wbGUgQElucHV0KCkgbWFwcGluZy5cclxuICAgKiBUaGUgZGVmYXVsdCBpbXBsZW1lbnRhdGlvbiBhdXRvbWF0aWNhbGx5IGFzc2lnbnMgaW5wdXQgZGF0YSB0byBjb21wb25lbnQgcHJvcGVydGllcy5cclxuICAgKiBcclxuICAgKiBAcGFyYW0gdyBUaGUgc2F2ZWQgd2lkZ2V0IGRhdGEgaW5jbHVkaW5nIGlucHV0IHByb3BlcnRpZXNcclxuICAgKiBcclxuICAgKiBAZXhhbXBsZVxyXG4gICAqIGBgYHR5cGVzY3JpcHRcclxuICAgKiBkZXNlcmlhbGl6ZSh3OiBOZ0dyaWRTdGFja1dpZGdldCkge1xyXG4gICAqICAgc3VwZXIuZGVzZXJpYWxpemUodyk7IC8vIENhbGwgcGFyZW50IGZvciBiYXNpYyBzZXR1cFxyXG4gICAqICAgXHJcbiAgICogICAvLyBDdXN0b20gaW5pdGlhbGl6YXRpb24gbG9naWNcclxuICAgKiAgIGlmICh3LmlucHV0Py5jb21wbGV4RGF0YSkge1xyXG4gICAqICAgICB0aGlzLnByb2Nlc3NDb21wbGV4RGF0YSh3LmlucHV0LmNvbXBsZXhEYXRhKTtcclxuICAgKiAgIH1cclxuICAgKiB9XHJcbiAgICogYGBgXHJcbiAgICovXHJcbiAgcHVibGljIGRlc2VyaWFsaXplKHc6IE5nR3JpZFN0YWNrV2lkZ2V0KSAge1xyXG4gICAgLy8gc2F2ZSBmdWxsIGRlc2NyaXB0aW9uIGZvciBtZXRhIGRhdGFcclxuICAgIHRoaXMud2lkZ2V0SXRlbSA9IHc7XHJcbiAgICBpZiAoIXcpIHJldHVybjtcclxuXHJcbiAgICBpZiAody5pbnB1dCkgIE9iamVjdC5hc3NpZ24odGhpcywgdy5pbnB1dCk7XHJcbiAgfVxyXG4gfVxyXG4iXX0=

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,53 @@
/**
* gridstack.component.ts 12.3.3
* Copyright (c) 2022-2024 Alain Dumesny - see GridStack root license
*/
import { NgModule } from "@angular/core";
import { GridstackItemComponent } from "./gridstack-item.component";
import { GridstackComponent } from "./gridstack.component";
import * as i0 from "@angular/core";
/**
* @deprecated Use GridstackComponent and GridstackItemComponent as standalone components instead.
*
* This NgModule is provided for backward compatibility but is no longer the recommended approach.
* Import components directly in your standalone components or use the new Angular module structure.
*
* @example
* ```typescript
* // Preferred approach - standalone components
* @Component({
* selector: 'my-app',
* imports: [GridstackComponent, GridstackItemComponent],
* template: '<gridstack></gridstack>'
* })
* export class AppComponent {}
*
* // Legacy approach (deprecated)
* @NgModule({
* imports: [GridstackModule]
* })
* export class AppModule {}
* ```
*/
export class GridstackModule {
}
GridstackModule.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: GridstackModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
GridstackModule.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "14.3.0", ngImport: i0, type: GridstackModule, imports: [GridstackItemComponent,
GridstackComponent], exports: [GridstackItemComponent,
GridstackComponent] });
GridstackModule.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: GridstackModule, imports: [GridstackItemComponent,
GridstackComponent] });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: GridstackModule, decorators: [{
type: NgModule,
args: [{
imports: [
GridstackItemComponent,
GridstackComponent,
],
exports: [
GridstackItemComponent,
GridstackComponent,
],
}]
}] });
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZ3JpZHN0YWNrLm1vZHVsZS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uL2FuZ3VsYXIvcHJvamVjdHMvbGliL3NyYy9saWIvZ3JpZHN0YWNrLm1vZHVsZS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQTs7O0dBR0c7QUFFSCxPQUFPLEVBQUUsUUFBUSxFQUFFLE1BQU0sZUFBZSxDQUFDO0FBRXpDLE9BQU8sRUFBRSxzQkFBc0IsRUFBRSxNQUFNLDRCQUE0QixDQUFDO0FBQ3BFLE9BQU8sRUFBRSxrQkFBa0IsRUFBRSxNQUFNLHVCQUF1QixDQUFDOztBQUUzRDs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztHQXNCRztBQVdILE1BQU0sT0FBTyxlQUFlOzs0R0FBZixlQUFlOzZHQUFmLGVBQWUsWUFSeEIsc0JBQXNCO1FBQ3RCLGtCQUFrQixhQUdsQixzQkFBc0I7UUFDdEIsa0JBQWtCOzZHQUdULGVBQWUsWUFSeEIsc0JBQXNCO1FBQ3RCLGtCQUFrQjsyRkFPVCxlQUFlO2tCQVYzQixRQUFRO21CQUFDO29CQUNSLE9BQU8sRUFBRTt3QkFDUCxzQkFBc0I7d0JBQ3RCLGtCQUFrQjtxQkFDbkI7b0JBQ0QsT0FBTyxFQUFFO3dCQUNQLHNCQUFzQjt3QkFDdEIsa0JBQWtCO3FCQUNuQjtpQkFDRiIsInNvdXJjZXNDb250ZW50IjpbIi8qKlxyXG4gKiBncmlkc3RhY2suY29tcG9uZW50LnRzIDEyLjMuM1xyXG4gKiBDb3B5cmlnaHQgKGMpIDIwMjItMjAyNCBBbGFpbiBEdW1lc255IC0gc2VlIEdyaWRTdGFjayByb290IGxpY2Vuc2VcclxuICovXHJcblxyXG5pbXBvcnQgeyBOZ01vZHVsZSB9IGZyb20gXCJAYW5ndWxhci9jb3JlXCI7XHJcblxyXG5pbXBvcnQgeyBHcmlkc3RhY2tJdGVtQ29tcG9uZW50IH0gZnJvbSBcIi4vZ3JpZHN0YWNrLWl0ZW0uY29tcG9uZW50XCI7XHJcbmltcG9ydCB7IEdyaWRzdGFja0NvbXBvbmVudCB9IGZyb20gXCIuL2dyaWRzdGFjay5jb21wb25lbnRcIjtcclxuXHJcbi8qKlxyXG4gKiBAZGVwcmVjYXRlZCBVc2UgR3JpZHN0YWNrQ29tcG9uZW50IGFuZCBHcmlkc3RhY2tJdGVtQ29tcG9uZW50IGFzIHN0YW5kYWxvbmUgY29tcG9uZW50cyBpbnN0ZWFkLlxyXG4gKiBcclxuICogVGhpcyBOZ01vZHVsZSBpcyBwcm92aWRlZCBmb3IgYmFja3dhcmQgY29tcGF0aWJpbGl0eSBidXQgaXMgbm8gbG9uZ2VyIHRoZSByZWNvbW1lbmRlZCBhcHByb2FjaC5cclxuICogSW1wb3J0IGNvbXBvbmVudHMgZGlyZWN0bHkgaW4geW91ciBzdGFuZGFsb25lIGNvbXBvbmVudHMgb3IgdXNlIHRoZSBuZXcgQW5ndWxhciBtb2R1bGUgc3RydWN0dXJlLlxyXG4gKiBcclxuICogQGV4YW1wbGVcclxuICogYGBgdHlwZXNjcmlwdFxyXG4gKiAvLyBQcmVmZXJyZWQgYXBwcm9hY2ggLSBzdGFuZGFsb25lIGNvbXBvbmVudHNcclxuICogQENvbXBvbmVudCh7XHJcbiAqICAgc2VsZWN0b3I6ICdteS1hcHAnLFxyXG4gKiAgIGltcG9ydHM6IFtHcmlkc3RhY2tDb21wb25lbnQsIEdyaWRzdGFja0l0ZW1Db21wb25lbnRdLFxyXG4gKiAgIHRlbXBsYXRlOiAnPGdyaWRzdGFjaz48L2dyaWRzdGFjaz4nXHJcbiAqIH0pXHJcbiAqIGV4cG9ydCBjbGFzcyBBcHBDb21wb25lbnQge31cclxuICogXHJcbiAqIC8vIExlZ2FjeSBhcHByb2FjaCAoZGVwcmVjYXRlZClcclxuICogQE5nTW9kdWxlKHtcclxuICogICBpbXBvcnRzOiBbR3JpZHN0YWNrTW9kdWxlXVxyXG4gKiB9KVxyXG4gKiBleHBvcnQgY2xhc3MgQXBwTW9kdWxlIHt9XHJcbiAqIGBgYFxyXG4gKi9cclxuQE5nTW9kdWxlKHtcclxuICBpbXBvcnRzOiBbXHJcbiAgICBHcmlkc3RhY2tJdGVtQ29tcG9uZW50LFxyXG4gICAgR3JpZHN0YWNrQ29tcG9uZW50LFxyXG4gIF0sXHJcbiAgZXhwb3J0czogW1xyXG4gICAgR3JpZHN0YWNrSXRlbUNvbXBvbmVudCxcclxuICAgIEdyaWRzdGFja0NvbXBvbmVudCxcclxuICBdLFxyXG59KVxyXG5leHBvcnQgY2xhc3MgR3JpZHN0YWNrTW9kdWxlIHt9XHJcbiJdfQ==

View File

@@ -0,0 +1,6 @@
/**
* gridstack-item.component.ts 12.3.3
* Copyright (c) 2025 Alain Dumesny - see GridStack root license
*/
export {};
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidHlwZXMuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi9hbmd1bGFyL3Byb2plY3RzL2xpYi9zcmMvbGliL3R5cGVzLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBOzs7R0FHRyIsInNvdXJjZXNDb250ZW50IjpbIi8qKlxyXG4gKiBncmlkc3RhY2staXRlbS5jb21wb25lbnQudHMgMTIuMy4zXHJcbiAqIENvcHlyaWdodCAoYykgMjAyNSBBbGFpbiBEdW1lc255IC0gc2VlIEdyaWRTdGFjayByb290IGxpY2Vuc2VcclxuICovXHJcblxyXG5pbXBvcnQgeyBHcmlkU3RhY2tOb2RlLCBHcmlkU3RhY2tPcHRpb25zLCBHcmlkU3RhY2tXaWRnZXQgfSBmcm9tIFwiZ3JpZHN0YWNrXCI7XHJcblxyXG4vKipcclxuICogRXh0ZW5kZWQgR3JpZFN0YWNrV2lkZ2V0IGludGVyZmFjZSBmb3IgQW5ndWxhciBpbnRlZ3JhdGlvbi5cclxuICogQWRkcyBBbmd1bGFyLXNwZWNpZmljIHByb3BlcnRpZXMgZm9yIGR5bmFtaWMgY29tcG9uZW50IGNyZWF0aW9uLlxyXG4gKi9cclxuZXhwb3J0IGludGVyZmFjZSBOZ0dyaWRTdGFja1dpZGdldCBleHRlbmRzIEdyaWRTdGFja1dpZGdldCB7XHJcbiAgLyoqIEFuZ3VsYXIgY29tcG9uZW50IHNlbGVjdG9yIGZvciBkeW5hbWljIGNyZWF0aW9uIChlLmcuLCAnbXktd2lkZ2V0JykgKi9cclxuICBzZWxlY3Rvcj86IHN0cmluZztcclxuICAvKiogU2VyaWFsaXplZCBkYXRhIGZvciBjb21wb25lbnQgQElucHV0KCkgcHJvcGVydGllcyAqL1xyXG4gIGlucHV0PzogTmdDb21wSW5wdXRzO1xyXG4gIC8qKiBDb25maWd1cmF0aW9uIGZvciBuZXN0ZWQgc3ViLWdyaWRzICovXHJcbiAgc3ViR3JpZE9wdHM/OiBOZ0dyaWRTdGFja09wdGlvbnM7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBFeHRlbmRlZCBHcmlkU3RhY2tOb2RlIGludGVyZmFjZSBmb3IgQW5ndWxhciBpbnRlZ3JhdGlvbi5cclxuICogQWRkcyBjb21wb25lbnQgc2VsZWN0b3IgZm9yIGR5bmFtaWMgY29udGVudCBjcmVhdGlvbi5cclxuICovXHJcbmV4cG9ydCBpbnRlcmZhY2UgTmdHcmlkU3RhY2tOb2RlIGV4dGVuZHMgR3JpZFN0YWNrTm9kZSB7XHJcbiAgLyoqIEFuZ3VsYXIgY29tcG9uZW50IHNlbGVjdG9yIGZvciB0aGlzIG5vZGUncyBjb250ZW50ICovXHJcbiAgc2VsZWN0b3I/OiBzdHJpbmc7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBFeHRlbmRlZCBHcmlkU3RhY2tPcHRpb25zIGludGVyZmFjZSBmb3IgQW5ndWxhciBpbnRlZ3JhdGlvbi5cclxuICogU3VwcG9ydHMgQW5ndWxhci1zcGVjaWZpYyB3aWRnZXQgZGVmaW5pdGlvbnMgYW5kIG5lc3RlZCBncmlkcy5cclxuICovXHJcbmV4cG9ydCBpbnRlcmZhY2UgTmdHcmlkU3RhY2tPcHRpb25zIGV4dGVuZHMgR3JpZFN0YWNrT3B0aW9ucyB7XHJcbiAgLyoqIEFycmF5IG9mIEFuZ3VsYXIgd2lkZ2V0IGRlZmluaXRpb25zIGZvciBpbml0aWFsIGdyaWQgc2V0dXAgKi9cclxuICBjaGlsZHJlbj86IE5nR3JpZFN0YWNrV2lkZ2V0W107XHJcbiAgLyoqIENvbmZpZ3VyYXRpb24gZm9yIG5lc3RlZCBzdWItZ3JpZHMgKEFuZ3VsYXItYXdhcmUpICovXHJcbiAgc3ViR3JpZE9wdHM/OiBOZ0dyaWRTdGFja09wdGlvbnM7XHJcbn1cclxuXHJcbi8qKlxyXG4gKiBUeXBlIGZvciBjb21wb25lbnQgaW5wdXQgZGF0YSBzZXJpYWxpemF0aW9uLlxyXG4gKiBNYXBzIEBJbnB1dCgpIHByb3BlcnR5IG5hbWVzIHRvIHRoZWlyIHZhbHVlcyBmb3Igd2lkZ2V0IHBlcnNpc3RlbmNlLlxyXG4gKiBcclxuICogQGV4YW1wbGVcclxuICogYGBgdHlwZXNjcmlwdFxyXG4gKiBjb25zdCBpbnB1dHM6IE5nQ29tcElucHV0cyA9IHtcclxuICogICB0aXRsZTogJ015IFdpZGdldCcsXHJcbiAqICAgdmFsdWU6IDQyLFxyXG4gKiAgIGNvbmZpZzogeyBlbmFibGVkOiB0cnVlIH1cclxuICogfTtcclxuICogYGBgXHJcbiAqL1xyXG5leHBvcnQgdHlwZSBOZ0NvbXBJbnB1dHMgPSB7W2tleTogc3RyaW5nXTogYW55fTtcclxuIl19

View File

@@ -0,0 +1,647 @@
import * as i0 from '@angular/core';
import { Injectable, ViewContainerRef, Component, ViewChild, Input, EventEmitter, reflectComponentType, ContentChildren, Output, NgModule } from '@angular/core';
import { NgIf } from '@angular/common';
import { GridStack } from 'gridstack';
/**
* gridstack-item.component.ts 12.3.3
* Copyright (c) 2022-2024 Alain Dumesny - see GridStack root license
*/
/**
* Base widget class for GridStack Angular integration.
*/
class BaseWidget {
/**
* Override this method to return serializable data for this widget.
*
* Return an object with properties that map to your component's @Input() fields.
* The selector is handled automatically, so only include component-specific data.
*
* @returns Object containing serializable component data
*
* @example
* ```typescript
* serialize() {
* return {
* title: this.title,
* value: this.value,
* settings: this.settings
* };
* }
* ```
*/
serialize() { return; }
/**
* Override this method to handle widget restoration from saved data.
*
* Use this for complex initialization that goes beyond simple @Input() mapping.
* The default implementation automatically assigns input data to component properties.
*
* @param w The saved widget data including input properties
*
* @example
* ```typescript
* deserialize(w: NgGridStackWidget) {
* super.deserialize(w); // Call parent for basic setup
*
* // Custom initialization logic
* if (w.input?.complexData) {
* this.processComplexData(w.input.complexData);
* }
* }
* ```
*/
deserialize(w) {
// save full description for meta data
this.widgetItem = w;
if (!w)
return;
if (w.input)
Object.assign(this, w.input);
}
}
BaseWidget.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: BaseWidget, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
BaseWidget.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: BaseWidget });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: BaseWidget, decorators: [{
type: Injectable
}] });
/**
* gridstack-item.component.ts 12.3.3
* Copyright (c) 2022-2024 Alain Dumesny - see GridStack root license
*/
/**
* Angular component wrapper for individual GridStack items.
*
* This component represents a single grid item and handles:
* - Dynamic content creation and management
* - Integration with parent GridStack component
* - Component lifecycle and cleanup
* - Widget options and configuration
*
* Use in combination with GridstackComponent for the parent grid.
*
* @example
* ```html
* <gridstack>
* <gridstack-item [options]="{x: 0, y: 0, w: 2, h: 1}">
* <my-widget-component></my-widget-component>
* </gridstack-item>
* </gridstack>
* ```
*/
class GridstackItemComponent {
constructor(elementRef) {
this.elementRef = elementRef;
this.el._gridItemComp = this;
}
/**
* Grid item configuration options.
* Defines position, size, and behavior of this grid item.
*
* @example
* ```typescript
* itemOptions: GridStackNode = {
* x: 0, y: 0, w: 2, h: 1,
* noResize: true,
* content: 'Item content'
* };
* ```
*/
set options(val) {
var _a;
const grid = (_a = this.el.gridstackNode) === null || _a === void 0 ? void 0 : _a.grid;
if (grid) {
// already built, do an update...
grid.update(this.el, val);
}
else {
// store our custom element in options so we can update it and not re-create a generic div!
this._options = Object.assign(Object.assign({}, val), { el: this.el });
}
}
/** return the latest grid options (from GS once built, otherwise initial values) */
get options() {
return this.el.gridstackNode || this._options || { el: this.el };
}
/** return the native element that contains grid specific fields as well */
get el() { return this.elementRef.nativeElement; }
/** clears the initial options now that we've built */
clearOptions() {
delete this._options;
}
ngOnDestroy() {
this.clearOptions();
delete this.childWidget;
delete this.el._gridItemComp;
delete this.container;
delete this.ref;
}
}
GridstackItemComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: GridstackItemComponent, deps: [{ token: i0.ElementRef }], target: i0.ɵɵFactoryTarget.Component });
GridstackItemComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "14.3.0", type: GridstackItemComponent, isStandalone: true, selector: "gridstack-item", inputs: { options: "options" }, viewQueries: [{ propertyName: "container", first: true, predicate: ["container"], descendants: true, read: ViewContainerRef, static: true }], ngImport: i0, template: `
<div class="grid-stack-item-content">
<!-- where dynamic items go based on component selector (recommended way), or sub-grids, etc...) -->
<ng-template #container></ng-template>
<!-- any static (defined in DOM - not recommended) content goes here -->
<ng-content></ng-content>
<!-- fallback HTML content from GridStackWidget.content if used instead (not recommended) -->
{{options.content}}
</div>`, isInline: true, styles: [":host{display:block}\n"] });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: GridstackItemComponent, decorators: [{
type: Component,
args: [{ selector: 'gridstack-item', template: `
<div class="grid-stack-item-content">
<!-- where dynamic items go based on component selector (recommended way), or sub-grids, etc...) -->
<ng-template #container></ng-template>
<!-- any static (defined in DOM - not recommended) content goes here -->
<ng-content></ng-content>
<!-- fallback HTML content from GridStackWidget.content if used instead (not recommended) -->
{{options.content}}
</div>`, standalone: true, styles: [":host{display:block}\n"] }]
}], ctorParameters: function () { return [{ type: i0.ElementRef }]; }, propDecorators: { container: [{
type: ViewChild,
args: ['container', { read: ViewContainerRef, static: true }]
}], options: [{
type: Input
}] } });
/**
* gridstack.component.ts 12.3.3
* Copyright (c) 2022-2024 Alain Dumesny - see GridStack root license
*/
/**
* Angular component wrapper for GridStack.
*
* This component provides Angular integration for GridStack grids, handling:
* - Grid initialization and lifecycle
* - Dynamic component creation and management
* - Event binding and emission
* - Integration with Angular change detection
*
* Use in combination with GridstackItemComponent for individual grid items.
*
* @example
* ```html
* <gridstack [options]="gridOptions" (change)="onGridChange($event)">
* <div empty-content>Drag widgets here</div>
* </gridstack>
* ```
*/
class GridstackComponent {
constructor(elementRef) {
this.elementRef = elementRef;
/**
* GridStack event emitters for Angular integration.
*
* These provide Angular-style event handling for GridStack events.
* Alternatively, use `this.grid.on('event1 event2', callback)` for multiple events.
*
* Note: 'CB' suffix prevents conflicts with native DOM events.
*
* @example
* ```html
* <gridstack (changeCB)="onGridChange($event)" (droppedCB)="onItemDropped($event)">
* </gridstack>
* ```
*/
/** Emitted when widgets are added to the grid */
this.addedCB = new EventEmitter();
/** Emitted when grid layout changes */
this.changeCB = new EventEmitter();
/** Emitted when grid is disabled */
this.disableCB = new EventEmitter();
/** Emitted during widget drag operations */
this.dragCB = new EventEmitter();
/** Emitted when widget drag starts */
this.dragStartCB = new EventEmitter();
/** Emitted when widget drag stops */
this.dragStopCB = new EventEmitter();
/** Emitted when widget is dropped */
this.droppedCB = new EventEmitter();
/** Emitted when grid is enabled */
this.enableCB = new EventEmitter();
/** Emitted when widgets are removed from the grid */
this.removedCB = new EventEmitter();
/** Emitted during widget resize operations */
this.resizeCB = new EventEmitter();
/** Emitted when widget resize starts */
this.resizeStartCB = new EventEmitter();
/** Emitted when widget resize stops */
this.resizeStopCB = new EventEmitter();
// set globally our method to create the right widget type
if (!GridStack.addRemoveCB) {
GridStack.addRemoveCB = gsCreateNgComponents;
}
if (!GridStack.saveCB) {
GridStack.saveCB = gsSaveAdditionalNgInfo;
}
if (!GridStack.updateCB) {
GridStack.updateCB = gsUpdateNgComponents;
}
this.el._gridComp = this;
}
/**
* Grid configuration options.
* Can be set before grid initialization or updated after grid is created.
*
* @example
* ```typescript
* gridOptions: GridStackOptions = {
* column: 12,
* cellHeight: 'auto',
* animate: true
* };
* ```
*/
set options(o) {
if (this._grid) {
this._grid.updateOptions(o);
}
else {
this._options = o;
}
}
/** Get the current running grid options */
get options() { var _a; return ((_a = this._grid) === null || _a === void 0 ? void 0 : _a.opts) || this._options || {}; }
/**
* Get the native DOM element that contains grid-specific fields.
* This element has GridStack properties attached to it.
*/
get el() { return this.elementRef.nativeElement; }
/**
* Get the underlying GridStack instance.
* Use this to access GridStack API methods directly.
*
* @example
* ```typescript
* this.gridComponent.grid.addWidget({x: 0, y: 0, w: 2, h: 1});
* ```
*/
get grid() { return this._grid; }
/**
* Register a list of Angular components for dynamic creation.
*
* @param typeList Array of component types to register
*
* @example
* ```typescript
* GridstackComponent.addComponentToSelectorType([
* MyWidgetComponent,
* AnotherWidgetComponent
* ]);
* ```
*/
static addComponentToSelectorType(typeList) {
typeList.forEach(type => GridstackComponent.selectorToType[GridstackComponent.getSelector(type)] = type);
}
/**
* Extract the selector string from an Angular component type.
*
* @param type The component type to get selector from
* @returns The component's selector string
*/
static getSelector(type) {
return reflectComponentType(type).selector;
}
ngOnInit() {
var _a, _b;
// init ourself before any template children are created since we track them below anyway - no need to double create+update widgets
this.loaded = !!((_b = (_a = this.options) === null || _a === void 0 ? void 0 : _a.children) === null || _b === void 0 ? void 0 : _b.length);
this._grid = GridStack.init(this._options, this.el);
delete this._options; // GS has it now
this.checkEmpty();
}
/** wait until after all DOM is ready to init gridstack children (after angular ngFor and sub-components run first) */
ngAfterContentInit() {
var _a;
// track whenever the children list changes and update the layout...
this._sub = (_a = this.gridstackItems) === null || _a === void 0 ? void 0 : _a.changes.subscribe(() => this.updateAll());
// ...and do this once at least unless we loaded children already
if (!this.loaded)
this.updateAll();
this.hookEvents(this.grid);
}
ngOnDestroy() {
var _a, _b;
this.unhookEvents(this._grid);
(_a = this._sub) === null || _a === void 0 ? void 0 : _a.unsubscribe();
(_b = this._grid) === null || _b === void 0 ? void 0 : _b.destroy();
delete this._grid;
delete this.el._gridComp;
delete this.container;
delete this.ref;
}
/**
* called when the TEMPLATE (not recommended) list of items changes - get a list of nodes and
* update the layout accordingly (which will take care of adding/removing items changed by Angular)
*/
updateAll() {
var _a;
if (!this.grid)
return;
const layout = [];
(_a = this.gridstackItems) === null || _a === void 0 ? void 0 : _a.forEach(item => {
layout.push(item.options);
item.clearOptions();
});
this.grid.load(layout); // efficient that does diffs only
}
/** check if the grid is empty, if so show alternative content */
checkEmpty() {
if (!this.grid)
return;
this.isEmpty = !this.grid.engine.nodes.length;
}
/** get all known events as easy to use Outputs for convenience */
hookEvents(grid) {
if (!grid)
return;
// nested grids don't have events in v12.1+ so skip
if (grid.parentGridNode)
return;
grid
.on('added', (event, nodes) => {
var _a;
const gridComp = ((_a = nodes[0].grid) === null || _a === void 0 ? void 0 : _a.el._gridComp) || this;
gridComp.checkEmpty();
this.addedCB.emit({ event, nodes });
})
.on('change', (event, nodes) => this.changeCB.emit({ event, nodes }))
.on('disable', (event) => this.disableCB.emit({ event }))
.on('drag', (event, el) => this.dragCB.emit({ event, el }))
.on('dragstart', (event, el) => this.dragStartCB.emit({ event, el }))
.on('dragstop', (event, el) => this.dragStopCB.emit({ event, el }))
.on('dropped', (event, previousNode, newNode) => this.droppedCB.emit({ event, previousNode, newNode }))
.on('enable', (event) => this.enableCB.emit({ event }))
.on('removed', (event, nodes) => {
var _a;
const gridComp = ((_a = nodes[0].grid) === null || _a === void 0 ? void 0 : _a.el._gridComp) || this;
gridComp.checkEmpty();
this.removedCB.emit({ event, nodes });
})
.on('resize', (event, el) => this.resizeCB.emit({ event, el }))
.on('resizestart', (event, el) => this.resizeStartCB.emit({ event, el }))
.on('resizestop', (event, el) => this.resizeStopCB.emit({ event, el }));
}
unhookEvents(grid) {
if (!grid)
return;
// nested grids don't have events in v12.1+ so skip
if (grid.parentGridNode)
return;
grid.off('added change disable drag dragstart dragstop dropped enable removed resize resizestart resizestop');
}
}
/**
* Mapping of component selectors to their types for dynamic creation.
*
* This enables dynamic component instantiation from string selectors.
* Angular doesn't provide public access to this mapping, so we maintain our own.
*
* @example
* ```typescript
* GridstackComponent.addComponentToSelectorType([MyWidgetComponent]);
* ```
*/
GridstackComponent.selectorToType = {};
GridstackComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: GridstackComponent, deps: [{ token: i0.ElementRef }], target: i0.ɵɵFactoryTarget.Component });
GridstackComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "14.3.0", type: GridstackComponent, isStandalone: true, selector: "gridstack", inputs: { options: "options", isEmpty: "isEmpty" }, outputs: { addedCB: "addedCB", changeCB: "changeCB", disableCB: "disableCB", dragCB: "dragCB", dragStartCB: "dragStartCB", dragStopCB: "dragStopCB", droppedCB: "droppedCB", enableCB: "enableCB", removedCB: "removedCB", resizeCB: "resizeCB", resizeStartCB: "resizeStartCB", resizeStopCB: "resizeStopCB" }, queries: [{ propertyName: "gridstackItems", predicate: GridstackItemComponent }], viewQueries: [{ propertyName: "container", first: true, predicate: ["container"], descendants: true, read: ViewContainerRef, static: true }], ngImport: i0, template: `
<!-- content to show when when grid is empty, like instructions on how to add widgets -->
<ng-content select="[empty-content]" *ngIf="isEmpty"></ng-content>
<!-- where dynamic items go -->
<ng-template #container></ng-template>
<!-- where template items go -->
<ng-content></ng-content>
`, isInline: true, styles: [":host{display:block}\n"], dependencies: [{ kind: "directive", type: NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }] });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: GridstackComponent, decorators: [{
type: Component,
args: [{ selector: 'gridstack', template: `
<!-- content to show when when grid is empty, like instructions on how to add widgets -->
<ng-content select="[empty-content]" *ngIf="isEmpty"></ng-content>
<!-- where dynamic items go -->
<ng-template #container></ng-template>
<!-- where template items go -->
<ng-content></ng-content>
`, standalone: true, imports: [NgIf], styles: [":host{display:block}\n"] }]
}], ctorParameters: function () { return [{ type: i0.ElementRef }]; }, propDecorators: { gridstackItems: [{
type: ContentChildren,
args: [GridstackItemComponent]
}], container: [{
type: ViewChild,
args: ['container', { read: ViewContainerRef, static: true }]
}], options: [{
type: Input
}], isEmpty: [{
type: Input
}], addedCB: [{
type: Output
}], changeCB: [{
type: Output
}], disableCB: [{
type: Output
}], dragCB: [{
type: Output
}], dragStartCB: [{
type: Output
}], dragStopCB: [{
type: Output
}], droppedCB: [{
type: Output
}], enableCB: [{
type: Output
}], removedCB: [{
type: Output
}], resizeCB: [{
type: Output
}], resizeStartCB: [{
type: Output
}], resizeStopCB: [{
type: Output
}] } });
/**
* can be used when a new item needs to be created, which we do as a Angular component, or deleted (skip)
**/
function gsCreateNgComponents(host, n, add, isGrid) {
var _a, _b, _c, _d, _e, _f, _g;
if (add) {
//
// create the component dynamically - see https://angular.io/docs/ts/latest/cookbook/dynamic-component-loader.html
//
if (!host)
return;
if (isGrid) {
// TODO: figure out how to create ng component inside regular Div. need to access app injectors...
// if (!container) {
// const hostElement: Element = host;
// const environmentInjector: EnvironmentInjector;
// grid = createComponent(GridstackComponent, {environmentInjector, hostElement})?.instance;
// }
const gridItemComp = (_a = host.parentElement) === null || _a === void 0 ? void 0 : _a._gridItemComp;
if (!gridItemComp)
return;
// check if gridItem has a child component with 'container' exposed to create under..
const container = ((_b = gridItemComp.childWidget) === null || _b === void 0 ? void 0 : _b.container) || gridItemComp.container;
const gridRef = container === null || container === void 0 ? void 0 : container.createComponent(GridstackComponent);
const grid = gridRef === null || gridRef === void 0 ? void 0 : gridRef.instance;
if (!grid)
return;
grid.ref = gridRef;
grid.options = n;
return grid.el;
}
else {
const gridComp = host._gridComp;
const gridItemRef = (_c = gridComp === null || gridComp === void 0 ? void 0 : gridComp.container) === null || _c === void 0 ? void 0 : _c.createComponent(GridstackItemComponent);
const gridItem = gridItemRef === null || gridItemRef === void 0 ? void 0 : gridItemRef.instance;
if (!gridItem)
return;
gridItem.ref = gridItemRef;
// define what type of component to create as child, OR you can do it GridstackItemComponent template, but this is more generic
const selector = n.selector;
const type = selector ? GridstackComponent.selectorToType[selector] : undefined;
if (type) {
// shared code to create our selector component
const createComp = () => {
var _a, _b;
const childWidget = (_b = (_a = gridItem.container) === null || _a === void 0 ? void 0 : _a.createComponent(type)) === null || _b === void 0 ? void 0 : _b.instance;
// if proper BaseWidget subclass, save it and load additional data
if (childWidget && typeof childWidget.serialize === 'function' && typeof childWidget.deserialize === 'function') {
gridItem.childWidget = childWidget;
childWidget.deserialize(n);
}
};
const lazyLoad = n.lazyLoad || ((_e = (_d = n.grid) === null || _d === void 0 ? void 0 : _d.opts) === null || _e === void 0 ? void 0 : _e.lazyLoad) && n.lazyLoad !== false;
if (lazyLoad) {
if (!n.visibleObservable) {
n.visibleObservable = new IntersectionObserver(([entry]) => {
var _a;
if (entry.isIntersecting) {
(_a = n.visibleObservable) === null || _a === void 0 ? void 0 : _a.disconnect();
delete n.visibleObservable;
createComp();
}
});
window.setTimeout(() => { var _a; return (_a = n.visibleObservable) === null || _a === void 0 ? void 0 : _a.observe(gridItem.el); }); // wait until callee sets position attributes
}
}
else
createComp();
}
return gridItem.el;
}
}
else {
//
// REMOVE - have to call ComponentRef:destroy() for dynamic objects to correctly remove themselves
// Note: this will destroy all children dynamic components as well: gridItem -> childWidget
//
if (isGrid) {
const grid = (_f = n.el) === null || _f === void 0 ? void 0 : _f._gridComp;
if (grid === null || grid === void 0 ? void 0 : grid.ref)
grid.ref.destroy();
else
grid === null || grid === void 0 ? void 0 : grid.ngOnDestroy();
}
else {
const gridItem = (_g = n.el) === null || _g === void 0 ? void 0 : _g._gridItemComp;
if (gridItem === null || gridItem === void 0 ? void 0 : gridItem.ref)
gridItem.ref.destroy();
else
gridItem === null || gridItem === void 0 ? void 0 : gridItem.ngOnDestroy();
}
}
return;
}
/**
* called for each item in the grid - check if additional information needs to be saved.
* Note: since this is options minus gridstack protected members using Utils.removeInternalForSave(),
* this typically doesn't need to do anything. However your custom Component @Input() are now supported
* using BaseWidget.serialize()
*/
function gsSaveAdditionalNgInfo(n, w) {
var _a, _b, _c;
const gridItem = (_a = n.el) === null || _a === void 0 ? void 0 : _a._gridItemComp;
if (gridItem) {
const input = (_b = gridItem.childWidget) === null || _b === void 0 ? void 0 : _b.serialize();
if (input) {
w.input = input;
}
return;
}
// else check if Grid
const grid = (_c = n.el) === null || _c === void 0 ? void 0 : _c._gridComp;
if (grid) {
//.... save any custom data
}
}
/**
* track when widgeta re updated (rather than created) to make sure we de-serialize them as well
*/
function gsUpdateNgComponents(n) {
var _a;
const w = n;
const gridItem = (_a = n.el) === null || _a === void 0 ? void 0 : _a._gridItemComp;
if ((gridItem === null || gridItem === void 0 ? void 0 : gridItem.childWidget) && w.input)
gridItem.childWidget.deserialize(w);
}
/**
* gridstack.component.ts 12.3.3
* Copyright (c) 2022-2024 Alain Dumesny - see GridStack root license
*/
/**
* @deprecated Use GridstackComponent and GridstackItemComponent as standalone components instead.
*
* This NgModule is provided for backward compatibility but is no longer the recommended approach.
* Import components directly in your standalone components or use the new Angular module structure.
*
* @example
* ```typescript
* // Preferred approach - standalone components
* @Component({
* selector: 'my-app',
* imports: [GridstackComponent, GridstackItemComponent],
* template: '<gridstack></gridstack>'
* })
* export class AppComponent {}
*
* // Legacy approach (deprecated)
* @NgModule({
* imports: [GridstackModule]
* })
* export class AppModule {}
* ```
*/
class GridstackModule {
}
GridstackModule.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: GridstackModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
GridstackModule.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "14.3.0", ngImport: i0, type: GridstackModule, imports: [GridstackItemComponent,
GridstackComponent], exports: [GridstackItemComponent,
GridstackComponent] });
GridstackModule.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: GridstackModule, imports: [GridstackItemComponent,
GridstackComponent] });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: GridstackModule, decorators: [{
type: NgModule,
args: [{
imports: [
GridstackItemComponent,
GridstackComponent,
],
exports: [
GridstackItemComponent,
GridstackComponent,
],
}]
}] });
/*
* Public API Surface of gridstack-angular
*/
/**
* Generated bundle index. Do not edit.
*/
export { BaseWidget, GridstackComponent, GridstackItemComponent, GridstackModule, gsCreateNgComponents, gsSaveAdditionalNgInfo, gsUpdateNgComponents };
//# sourceMappingURL=gridstack-angular.mjs.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,640 @@
import * as i0 from '@angular/core';
import { Injectable, ViewContainerRef, Component, ViewChild, Input, EventEmitter, reflectComponentType, ContentChildren, Output, NgModule } from '@angular/core';
import { NgIf } from '@angular/common';
import { GridStack } from 'gridstack';
/**
* gridstack-item.component.ts 12.3.3
* Copyright (c) 2025 Alain Dumesny - see GridStack root license
*/
/**
* gridstack-item.component.ts 12.3.3
* Copyright (c) 2022-2024 Alain Dumesny - see GridStack root license
*/
/**
* Base widget class for GridStack Angular integration.
*/
class BaseWidget {
/**
* Override this method to return serializable data for this widget.
*
* Return an object with properties that map to your component's @Input() fields.
* The selector is handled automatically, so only include component-specific data.
*
* @returns Object containing serializable component data
*
* @example
* ```typescript
* serialize() {
* return {
* title: this.title,
* value: this.value,
* settings: this.settings
* };
* }
* ```
*/
serialize() { return; }
/**
* Override this method to handle widget restoration from saved data.
*
* Use this for complex initialization that goes beyond simple @Input() mapping.
* The default implementation automatically assigns input data to component properties.
*
* @param w The saved widget data including input properties
*
* @example
* ```typescript
* deserialize(w: NgGridStackWidget) {
* super.deserialize(w); // Call parent for basic setup
*
* // Custom initialization logic
* if (w.input?.complexData) {
* this.processComplexData(w.input.complexData);
* }
* }
* ```
*/
deserialize(w) {
// save full description for meta data
this.widgetItem = w;
if (!w)
return;
if (w.input)
Object.assign(this, w.input);
}
}
BaseWidget.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: BaseWidget, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
BaseWidget.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: BaseWidget });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: BaseWidget, decorators: [{
type: Injectable
}] });
/**
* gridstack-item.component.ts 12.3.3
* Copyright (c) 2022-2024 Alain Dumesny - see GridStack root license
*/
/**
* Angular component wrapper for individual GridStack items.
*
* This component represents a single grid item and handles:
* - Dynamic content creation and management
* - Integration with parent GridStack component
* - Component lifecycle and cleanup
* - Widget options and configuration
*
* Use in combination with GridstackComponent for the parent grid.
*
* @example
* ```html
* <gridstack>
* <gridstack-item [options]="{x: 0, y: 0, w: 2, h: 1}">
* <my-widget-component></my-widget-component>
* </gridstack-item>
* </gridstack>
* ```
*/
class GridstackItemComponent {
constructor(elementRef) {
this.elementRef = elementRef;
this.el._gridItemComp = this;
}
/**
* Grid item configuration options.
* Defines position, size, and behavior of this grid item.
*
* @example
* ```typescript
* itemOptions: GridStackNode = {
* x: 0, y: 0, w: 2, h: 1,
* noResize: true,
* content: 'Item content'
* };
* ```
*/
set options(val) {
const grid = this.el.gridstackNode?.grid;
if (grid) {
// already built, do an update...
grid.update(this.el, val);
}
else {
// store our custom element in options so we can update it and not re-create a generic div!
this._options = { ...val, el: this.el };
}
}
/** return the latest grid options (from GS once built, otherwise initial values) */
get options() {
return this.el.gridstackNode || this._options || { el: this.el };
}
/** return the native element that contains grid specific fields as well */
get el() { return this.elementRef.nativeElement; }
/** clears the initial options now that we've built */
clearOptions() {
delete this._options;
}
ngOnDestroy() {
this.clearOptions();
delete this.childWidget;
delete this.el._gridItemComp;
delete this.container;
delete this.ref;
}
}
GridstackItemComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: GridstackItemComponent, deps: [{ token: i0.ElementRef }], target: i0.ɵɵFactoryTarget.Component });
GridstackItemComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "14.3.0", type: GridstackItemComponent, isStandalone: true, selector: "gridstack-item", inputs: { options: "options" }, viewQueries: [{ propertyName: "container", first: true, predicate: ["container"], descendants: true, read: ViewContainerRef, static: true }], ngImport: i0, template: `
<div class="grid-stack-item-content">
<!-- where dynamic items go based on component selector (recommended way), or sub-grids, etc...) -->
<ng-template #container></ng-template>
<!-- any static (defined in DOM - not recommended) content goes here -->
<ng-content></ng-content>
<!-- fallback HTML content from GridStackWidget.content if used instead (not recommended) -->
{{options.content}}
</div>`, isInline: true, styles: [":host{display:block}\n"] });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: GridstackItemComponent, decorators: [{
type: Component,
args: [{ selector: 'gridstack-item', template: `
<div class="grid-stack-item-content">
<!-- where dynamic items go based on component selector (recommended way), or sub-grids, etc...) -->
<ng-template #container></ng-template>
<!-- any static (defined in DOM - not recommended) content goes here -->
<ng-content></ng-content>
<!-- fallback HTML content from GridStackWidget.content if used instead (not recommended) -->
{{options.content}}
</div>`, standalone: true, styles: [":host{display:block}\n"] }]
}], ctorParameters: function () { return [{ type: i0.ElementRef }]; }, propDecorators: { container: [{
type: ViewChild,
args: ['container', { read: ViewContainerRef, static: true }]
}], options: [{
type: Input
}] } });
/**
* gridstack.component.ts 12.3.3
* Copyright (c) 2022-2024 Alain Dumesny - see GridStack root license
*/
/**
* Angular component wrapper for GridStack.
*
* This component provides Angular integration for GridStack grids, handling:
* - Grid initialization and lifecycle
* - Dynamic component creation and management
* - Event binding and emission
* - Integration with Angular change detection
*
* Use in combination with GridstackItemComponent for individual grid items.
*
* @example
* ```html
* <gridstack [options]="gridOptions" (change)="onGridChange($event)">
* <div empty-content>Drag widgets here</div>
* </gridstack>
* ```
*/
class GridstackComponent {
constructor(elementRef) {
this.elementRef = elementRef;
/**
* GridStack event emitters for Angular integration.
*
* These provide Angular-style event handling for GridStack events.
* Alternatively, use `this.grid.on('event1 event2', callback)` for multiple events.
*
* Note: 'CB' suffix prevents conflicts with native DOM events.
*
* @example
* ```html
* <gridstack (changeCB)="onGridChange($event)" (droppedCB)="onItemDropped($event)">
* </gridstack>
* ```
*/
/** Emitted when widgets are added to the grid */
this.addedCB = new EventEmitter();
/** Emitted when grid layout changes */
this.changeCB = new EventEmitter();
/** Emitted when grid is disabled */
this.disableCB = new EventEmitter();
/** Emitted during widget drag operations */
this.dragCB = new EventEmitter();
/** Emitted when widget drag starts */
this.dragStartCB = new EventEmitter();
/** Emitted when widget drag stops */
this.dragStopCB = new EventEmitter();
/** Emitted when widget is dropped */
this.droppedCB = new EventEmitter();
/** Emitted when grid is enabled */
this.enableCB = new EventEmitter();
/** Emitted when widgets are removed from the grid */
this.removedCB = new EventEmitter();
/** Emitted during widget resize operations */
this.resizeCB = new EventEmitter();
/** Emitted when widget resize starts */
this.resizeStartCB = new EventEmitter();
/** Emitted when widget resize stops */
this.resizeStopCB = new EventEmitter();
// set globally our method to create the right widget type
if (!GridStack.addRemoveCB) {
GridStack.addRemoveCB = gsCreateNgComponents;
}
if (!GridStack.saveCB) {
GridStack.saveCB = gsSaveAdditionalNgInfo;
}
if (!GridStack.updateCB) {
GridStack.updateCB = gsUpdateNgComponents;
}
this.el._gridComp = this;
}
/**
* Grid configuration options.
* Can be set before grid initialization or updated after grid is created.
*
* @example
* ```typescript
* gridOptions: GridStackOptions = {
* column: 12,
* cellHeight: 'auto',
* animate: true
* };
* ```
*/
set options(o) {
if (this._grid) {
this._grid.updateOptions(o);
}
else {
this._options = o;
}
}
/** Get the current running grid options */
get options() { return this._grid?.opts || this._options || {}; }
/**
* Get the native DOM element that contains grid-specific fields.
* This element has GridStack properties attached to it.
*/
get el() { return this.elementRef.nativeElement; }
/**
* Get the underlying GridStack instance.
* Use this to access GridStack API methods directly.
*
* @example
* ```typescript
* this.gridComponent.grid.addWidget({x: 0, y: 0, w: 2, h: 1});
* ```
*/
get grid() { return this._grid; }
/**
* Register a list of Angular components for dynamic creation.
*
* @param typeList Array of component types to register
*
* @example
* ```typescript
* GridstackComponent.addComponentToSelectorType([
* MyWidgetComponent,
* AnotherWidgetComponent
* ]);
* ```
*/
static addComponentToSelectorType(typeList) {
typeList.forEach(type => GridstackComponent.selectorToType[GridstackComponent.getSelector(type)] = type);
}
/**
* Extract the selector string from an Angular component type.
*
* @param type The component type to get selector from
* @returns The component's selector string
*/
static getSelector(type) {
return reflectComponentType(type).selector;
}
ngOnInit() {
// init ourself before any template children are created since we track them below anyway - no need to double create+update widgets
this.loaded = !!this.options?.children?.length;
this._grid = GridStack.init(this._options, this.el);
delete this._options; // GS has it now
this.checkEmpty();
}
/** wait until after all DOM is ready to init gridstack children (after angular ngFor and sub-components run first) */
ngAfterContentInit() {
// track whenever the children list changes and update the layout...
this._sub = this.gridstackItems?.changes.subscribe(() => this.updateAll());
// ...and do this once at least unless we loaded children already
if (!this.loaded)
this.updateAll();
this.hookEvents(this.grid);
}
ngOnDestroy() {
this.unhookEvents(this._grid);
this._sub?.unsubscribe();
this._grid?.destroy();
delete this._grid;
delete this.el._gridComp;
delete this.container;
delete this.ref;
}
/**
* called when the TEMPLATE (not recommended) list of items changes - get a list of nodes and
* update the layout accordingly (which will take care of adding/removing items changed by Angular)
*/
updateAll() {
if (!this.grid)
return;
const layout = [];
this.gridstackItems?.forEach(item => {
layout.push(item.options);
item.clearOptions();
});
this.grid.load(layout); // efficient that does diffs only
}
/** check if the grid is empty, if so show alternative content */
checkEmpty() {
if (!this.grid)
return;
this.isEmpty = !this.grid.engine.nodes.length;
}
/** get all known events as easy to use Outputs for convenience */
hookEvents(grid) {
if (!grid)
return;
// nested grids don't have events in v12.1+ so skip
if (grid.parentGridNode)
return;
grid
.on('added', (event, nodes) => {
const gridComp = nodes[0].grid?.el._gridComp || this;
gridComp.checkEmpty();
this.addedCB.emit({ event, nodes });
})
.on('change', (event, nodes) => this.changeCB.emit({ event, nodes }))
.on('disable', (event) => this.disableCB.emit({ event }))
.on('drag', (event, el) => this.dragCB.emit({ event, el }))
.on('dragstart', (event, el) => this.dragStartCB.emit({ event, el }))
.on('dragstop', (event, el) => this.dragStopCB.emit({ event, el }))
.on('dropped', (event, previousNode, newNode) => this.droppedCB.emit({ event, previousNode, newNode }))
.on('enable', (event) => this.enableCB.emit({ event }))
.on('removed', (event, nodes) => {
const gridComp = nodes[0].grid?.el._gridComp || this;
gridComp.checkEmpty();
this.removedCB.emit({ event, nodes });
})
.on('resize', (event, el) => this.resizeCB.emit({ event, el }))
.on('resizestart', (event, el) => this.resizeStartCB.emit({ event, el }))
.on('resizestop', (event, el) => this.resizeStopCB.emit({ event, el }));
}
unhookEvents(grid) {
if (!grid)
return;
// nested grids don't have events in v12.1+ so skip
if (grid.parentGridNode)
return;
grid.off('added change disable drag dragstart dragstop dropped enable removed resize resizestart resizestop');
}
}
/**
* Mapping of component selectors to their types for dynamic creation.
*
* This enables dynamic component instantiation from string selectors.
* Angular doesn't provide public access to this mapping, so we maintain our own.
*
* @example
* ```typescript
* GridstackComponent.addComponentToSelectorType([MyWidgetComponent]);
* ```
*/
GridstackComponent.selectorToType = {};
GridstackComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: GridstackComponent, deps: [{ token: i0.ElementRef }], target: i0.ɵɵFactoryTarget.Component });
GridstackComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "14.3.0", type: GridstackComponent, isStandalone: true, selector: "gridstack", inputs: { options: "options", isEmpty: "isEmpty" }, outputs: { addedCB: "addedCB", changeCB: "changeCB", disableCB: "disableCB", dragCB: "dragCB", dragStartCB: "dragStartCB", dragStopCB: "dragStopCB", droppedCB: "droppedCB", enableCB: "enableCB", removedCB: "removedCB", resizeCB: "resizeCB", resizeStartCB: "resizeStartCB", resizeStopCB: "resizeStopCB" }, queries: [{ propertyName: "gridstackItems", predicate: GridstackItemComponent }], viewQueries: [{ propertyName: "container", first: true, predicate: ["container"], descendants: true, read: ViewContainerRef, static: true }], ngImport: i0, template: `
<!-- content to show when when grid is empty, like instructions on how to add widgets -->
<ng-content select="[empty-content]" *ngIf="isEmpty"></ng-content>
<!-- where dynamic items go -->
<ng-template #container></ng-template>
<!-- where template items go -->
<ng-content></ng-content>
`, isInline: true, styles: [":host{display:block}\n"], dependencies: [{ kind: "directive", type: NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }] });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: GridstackComponent, decorators: [{
type: Component,
args: [{ selector: 'gridstack', template: `
<!-- content to show when when grid is empty, like instructions on how to add widgets -->
<ng-content select="[empty-content]" *ngIf="isEmpty"></ng-content>
<!-- where dynamic items go -->
<ng-template #container></ng-template>
<!-- where template items go -->
<ng-content></ng-content>
`, standalone: true, imports: [NgIf], styles: [":host{display:block}\n"] }]
}], ctorParameters: function () { return [{ type: i0.ElementRef }]; }, propDecorators: { gridstackItems: [{
type: ContentChildren,
args: [GridstackItemComponent]
}], container: [{
type: ViewChild,
args: ['container', { read: ViewContainerRef, static: true }]
}], options: [{
type: Input
}], isEmpty: [{
type: Input
}], addedCB: [{
type: Output
}], changeCB: [{
type: Output
}], disableCB: [{
type: Output
}], dragCB: [{
type: Output
}], dragStartCB: [{
type: Output
}], dragStopCB: [{
type: Output
}], droppedCB: [{
type: Output
}], enableCB: [{
type: Output
}], removedCB: [{
type: Output
}], resizeCB: [{
type: Output
}], resizeStartCB: [{
type: Output
}], resizeStopCB: [{
type: Output
}] } });
/**
* can be used when a new item needs to be created, which we do as a Angular component, or deleted (skip)
**/
function gsCreateNgComponents(host, n, add, isGrid) {
if (add) {
//
// create the component dynamically - see https://angular.io/docs/ts/latest/cookbook/dynamic-component-loader.html
//
if (!host)
return;
if (isGrid) {
// TODO: figure out how to create ng component inside regular Div. need to access app injectors...
// if (!container) {
// const hostElement: Element = host;
// const environmentInjector: EnvironmentInjector;
// grid = createComponent(GridstackComponent, {environmentInjector, hostElement})?.instance;
// }
const gridItemComp = host.parentElement?._gridItemComp;
if (!gridItemComp)
return;
// check if gridItem has a child component with 'container' exposed to create under..
const container = gridItemComp.childWidget?.container || gridItemComp.container;
const gridRef = container?.createComponent(GridstackComponent);
const grid = gridRef?.instance;
if (!grid)
return;
grid.ref = gridRef;
grid.options = n;
return grid.el;
}
else {
const gridComp = host._gridComp;
const gridItemRef = gridComp?.container?.createComponent(GridstackItemComponent);
const gridItem = gridItemRef?.instance;
if (!gridItem)
return;
gridItem.ref = gridItemRef;
// define what type of component to create as child, OR you can do it GridstackItemComponent template, but this is more generic
const selector = n.selector;
const type = selector ? GridstackComponent.selectorToType[selector] : undefined;
if (type) {
// shared code to create our selector component
const createComp = () => {
const childWidget = gridItem.container?.createComponent(type)?.instance;
// if proper BaseWidget subclass, save it and load additional data
if (childWidget && typeof childWidget.serialize === 'function' && typeof childWidget.deserialize === 'function') {
gridItem.childWidget = childWidget;
childWidget.deserialize(n);
}
};
const lazyLoad = n.lazyLoad || n.grid?.opts?.lazyLoad && n.lazyLoad !== false;
if (lazyLoad) {
if (!n.visibleObservable) {
n.visibleObservable = new IntersectionObserver(([entry]) => {
if (entry.isIntersecting) {
n.visibleObservable?.disconnect();
delete n.visibleObservable;
createComp();
}
});
window.setTimeout(() => n.visibleObservable?.observe(gridItem.el)); // wait until callee sets position attributes
}
}
else
createComp();
}
return gridItem.el;
}
}
else {
//
// REMOVE - have to call ComponentRef:destroy() for dynamic objects to correctly remove themselves
// Note: this will destroy all children dynamic components as well: gridItem -> childWidget
//
if (isGrid) {
const grid = n.el?._gridComp;
if (grid?.ref)
grid.ref.destroy();
else
grid?.ngOnDestroy();
}
else {
const gridItem = n.el?._gridItemComp;
if (gridItem?.ref)
gridItem.ref.destroy();
else
gridItem?.ngOnDestroy();
}
}
return;
}
/**
* called for each item in the grid - check if additional information needs to be saved.
* Note: since this is options minus gridstack protected members using Utils.removeInternalForSave(),
* this typically doesn't need to do anything. However your custom Component @Input() are now supported
* using BaseWidget.serialize()
*/
function gsSaveAdditionalNgInfo(n, w) {
const gridItem = n.el?._gridItemComp;
if (gridItem) {
const input = gridItem.childWidget?.serialize();
if (input) {
w.input = input;
}
return;
}
// else check if Grid
const grid = n.el?._gridComp;
if (grid) {
//.... save any custom data
}
}
/**
* track when widgeta re updated (rather than created) to make sure we de-serialize them as well
*/
function gsUpdateNgComponents(n) {
const w = n;
const gridItem = n.el?._gridItemComp;
if (gridItem?.childWidget && w.input)
gridItem.childWidget.deserialize(w);
}
/**
* gridstack.component.ts 12.3.3
* Copyright (c) 2022-2024 Alain Dumesny - see GridStack root license
*/
/**
* @deprecated Use GridstackComponent and GridstackItemComponent as standalone components instead.
*
* This NgModule is provided for backward compatibility but is no longer the recommended approach.
* Import components directly in your standalone components or use the new Angular module structure.
*
* @example
* ```typescript
* // Preferred approach - standalone components
* @Component({
* selector: 'my-app',
* imports: [GridstackComponent, GridstackItemComponent],
* template: '<gridstack></gridstack>'
* })
* export class AppComponent {}
*
* // Legacy approach (deprecated)
* @NgModule({
* imports: [GridstackModule]
* })
* export class AppModule {}
* ```
*/
class GridstackModule {
}
GridstackModule.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: GridstackModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
GridstackModule.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "14.3.0", ngImport: i0, type: GridstackModule, imports: [GridstackItemComponent,
GridstackComponent], exports: [GridstackItemComponent,
GridstackComponent] });
GridstackModule.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: GridstackModule, imports: [GridstackItemComponent,
GridstackComponent] });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: GridstackModule, decorators: [{
type: NgModule,
args: [{
imports: [
GridstackItemComponent,
GridstackComponent,
],
exports: [
GridstackItemComponent,
GridstackComponent,
],
}]
}] });
/*
* Public API Surface of gridstack-angular
*/
/**
* Generated bundle index. Do not edit.
*/
export { BaseWidget, GridstackComponent, GridstackItemComponent, GridstackModule, gsCreateNgComponents, gsSaveAdditionalNgInfo, gsUpdateNgComponents };
//# sourceMappingURL=gridstack-angular.mjs.map

File diff suppressed because one or more lines are too long

5
node_modules/gridstack/dist/angular/index.d.ts generated vendored Normal file
View File

@@ -0,0 +1,5 @@
export * from './lib/types';
export * from './lib/base-widget';
export * from './lib/gridstack-item.component';
export * from './lib/gridstack.component';
export * from './lib/gridstack.module';

View File

@@ -0,0 +1,55 @@
import { NgCompInputs, NgGridStackWidget } from './types';
import * as i0 from "@angular/core";
/**
* Base widget class for GridStack Angular integration.
*/
export declare abstract class BaseWidget {
/**
* Complete widget definition including position, size, and Angular-specific data.
* Populated automatically when the widget is loaded or saved.
*/
widgetItem?: NgGridStackWidget;
/**
* Override this method to return serializable data for this widget.
*
* Return an object with properties that map to your component's @Input() fields.
* The selector is handled automatically, so only include component-specific data.
*
* @returns Object containing serializable component data
*
* @example
* ```typescript
* serialize() {
* return {
* title: this.title,
* value: this.value,
* settings: this.settings
* };
* }
* ```
*/
serialize(): NgCompInputs | undefined;
/**
* Override this method to handle widget restoration from saved data.
*
* Use this for complex initialization that goes beyond simple @Input() mapping.
* The default implementation automatically assigns input data to component properties.
*
* @param w The saved widget data including input properties
*
* @example
* ```typescript
* deserialize(w: NgGridStackWidget) {
* super.deserialize(w); // Call parent for basic setup
*
* // Custom initialization logic
* if (w.input?.complexData) {
* this.processComplexData(w.input.complexData);
* }
* }
* ```
*/
deserialize(w: NgGridStackWidget): void;
static ɵfac: i0.ɵɵFactoryDeclaration<BaseWidget, never>;
static ɵprov: i0.ɵɵInjectableDeclaration<BaseWidget>;
}

View File

@@ -0,0 +1,79 @@
/**
* gridstack-item.component.ts 12.3.3
* Copyright (c) 2022-2024 Alain Dumesny - see GridStack root license
*/
import { ElementRef, ViewContainerRef, OnDestroy, ComponentRef } from '@angular/core';
import { GridItemHTMLElement, GridStackNode } from 'gridstack';
import { BaseWidget } from './base-widget';
import * as i0 from "@angular/core";
/**
* Extended HTMLElement interface for grid items.
* Stores a back-reference to the Angular component for integration.
*/
export interface GridItemCompHTMLElement extends GridItemHTMLElement {
/** Back-reference to the Angular GridStackItem component */
_gridItemComp?: GridstackItemComponent;
}
/**
* Angular component wrapper for individual GridStack items.
*
* This component represents a single grid item and handles:
* - Dynamic content creation and management
* - Integration with parent GridStack component
* - Component lifecycle and cleanup
* - Widget options and configuration
*
* Use in combination with GridstackComponent for the parent grid.
*
* @example
* ```html
* <gridstack>
* <gridstack-item [options]="{x: 0, y: 0, w: 2, h: 1}">
* <my-widget-component></my-widget-component>
* </gridstack-item>
* </gridstack>
* ```
*/
export declare class GridstackItemComponent implements OnDestroy {
protected readonly elementRef: ElementRef<GridItemCompHTMLElement>;
/**
* Container for dynamic component creation within this grid item.
* Used to append child components programmatically.
*/
container?: ViewContainerRef;
/**
* Component reference for dynamic component removal.
* Used internally when this component is created dynamically.
*/
ref: ComponentRef<GridstackItemComponent> | undefined;
/**
* Reference to child widget component for serialization.
* Used to save/restore additional data along with grid position.
*/
childWidget: BaseWidget | undefined;
/**
* Grid item configuration options.
* Defines position, size, and behavior of this grid item.
*
* @example
* ```typescript
* itemOptions: GridStackNode = {
* x: 0, y: 0, w: 2, h: 1,
* noResize: true,
* content: 'Item content'
* };
* ```
*/
set options(val: GridStackNode);
/** return the latest grid options (from GS once built, otherwise initial values) */
get options(): GridStackNode;
protected _options?: GridStackNode;
/** return the native element that contains grid specific fields as well */
get el(): GridItemCompHTMLElement;
/** clears the initial options now that we've built */
clearOptions(): void;
constructor(elementRef: ElementRef<GridItemCompHTMLElement>);
ngOnDestroy(): void;
static ɵfac: i0.ɵɵFactoryDeclaration<GridstackItemComponent, never>;
static ɵcmp: i0.ɵɵComponentDeclaration<GridstackItemComponent, "gridstack-item", never, { "options": "options"; }, {}, never, ["*"], true>;
}

View File

@@ -0,0 +1,236 @@
/**
* gridstack.component.ts 12.3.3
* Copyright (c) 2022-2024 Alain Dumesny - see GridStack root license
*/
import { AfterContentInit, ElementRef, EventEmitter, OnDestroy, OnInit, QueryList, Type, ViewContainerRef, ComponentRef } from '@angular/core';
import { Subscription } from 'rxjs';
import { GridHTMLElement, GridItemHTMLElement, GridStack, GridStackNode, GridStackOptions } from 'gridstack';
import { NgGridStackNode, NgGridStackWidget } from './types';
import { GridstackItemComponent } from './gridstack-item.component';
import * as i0 from "@angular/core";
/**
* Event handler callback signatures for different GridStack events.
* These types define the structure of data passed to Angular event emitters.
*/
/** Callback for general events (enable, disable, etc.) */
export declare type eventCB = {
event: Event;
};
/** Callback for element-specific events (resize, drag, etc.) */
export declare type elementCB = {
event: Event;
el: GridItemHTMLElement;
};
/** Callback for events affecting multiple nodes (change, etc.) */
export declare type nodesCB = {
event: Event;
nodes: GridStackNode[];
};
/** Callback for drop events with before/after node state */
export declare type droppedCB = {
event: Event;
previousNode: GridStackNode;
newNode: GridStackNode;
};
/**
* Extended HTMLElement interface for the grid container.
* Stores a back-reference to the Angular component for integration purposes.
*/
export interface GridCompHTMLElement extends GridHTMLElement {
/** Back-reference to the Angular GridStack component */
_gridComp?: GridstackComponent;
}
/**
* Mapping of selector strings to Angular component types.
* Used for dynamic component creation based on widget selectors.
*/
export declare type SelectorToType = {
[key: string]: Type<Object>;
};
/**
* Angular component wrapper for GridStack.
*
* This component provides Angular integration for GridStack grids, handling:
* - Grid initialization and lifecycle
* - Dynamic component creation and management
* - Event binding and emission
* - Integration with Angular change detection
*
* Use in combination with GridstackItemComponent for individual grid items.
*
* @example
* ```html
* <gridstack [options]="gridOptions" (change)="onGridChange($event)">
* <div empty-content>Drag widgets here</div>
* </gridstack>
* ```
*/
export declare class GridstackComponent implements OnInit, AfterContentInit, OnDestroy {
protected readonly elementRef: ElementRef<GridCompHTMLElement>;
/**
* List of template-based grid items (not recommended approach).
* Used to sync between DOM and GridStack internals when items are defined in templates.
* Prefer dynamic component creation instead.
*/
gridstackItems?: QueryList<GridstackItemComponent>;
/**
* Container for dynamic component creation (recommended approach).
* Used to append grid items programmatically at runtime.
*/
container?: ViewContainerRef;
/**
* Grid configuration options.
* Can be set before grid initialization or updated after grid is created.
*
* @example
* ```typescript
* gridOptions: GridStackOptions = {
* column: 12,
* cellHeight: 'auto',
* animate: true
* };
* ```
*/
set options(o: GridStackOptions);
/** Get the current running grid options */
get options(): GridStackOptions;
/**
* Controls whether empty content should be displayed.
* Set to true to show ng-content with 'empty-content' selector when grid has no items.
*
* @example
* ```html
* <gridstack [isEmpty]="gridItems.length === 0">
* <div empty-content>Drag widgets here to get started</div>
* </gridstack>
* ```
*/
isEmpty?: boolean;
/**
* GridStack event emitters for Angular integration.
*
* These provide Angular-style event handling for GridStack events.
* Alternatively, use `this.grid.on('event1 event2', callback)` for multiple events.
*
* Note: 'CB' suffix prevents conflicts with native DOM events.
*
* @example
* ```html
* <gridstack (changeCB)="onGridChange($event)" (droppedCB)="onItemDropped($event)">
* </gridstack>
* ```
*/
/** Emitted when widgets are added to the grid */
addedCB: EventEmitter<nodesCB>;
/** Emitted when grid layout changes */
changeCB: EventEmitter<nodesCB>;
/** Emitted when grid is disabled */
disableCB: EventEmitter<eventCB>;
/** Emitted during widget drag operations */
dragCB: EventEmitter<elementCB>;
/** Emitted when widget drag starts */
dragStartCB: EventEmitter<elementCB>;
/** Emitted when widget drag stops */
dragStopCB: EventEmitter<elementCB>;
/** Emitted when widget is dropped */
droppedCB: EventEmitter<droppedCB>;
/** Emitted when grid is enabled */
enableCB: EventEmitter<eventCB>;
/** Emitted when widgets are removed from the grid */
removedCB: EventEmitter<nodesCB>;
/** Emitted during widget resize operations */
resizeCB: EventEmitter<elementCB>;
/** Emitted when widget resize starts */
resizeStartCB: EventEmitter<elementCB>;
/** Emitted when widget resize stops */
resizeStopCB: EventEmitter<elementCB>;
/**
* Get the native DOM element that contains grid-specific fields.
* This element has GridStack properties attached to it.
*/
get el(): GridCompHTMLElement;
/**
* Get the underlying GridStack instance.
* Use this to access GridStack API methods directly.
*
* @example
* ```typescript
* this.gridComponent.grid.addWidget({x: 0, y: 0, w: 2, h: 1});
* ```
*/
get grid(): GridStack | undefined;
/**
* Component reference for dynamic component removal.
* Used internally when this component is created dynamically.
*/
ref: ComponentRef<GridstackComponent> | undefined;
/**
* Mapping of component selectors to their types for dynamic creation.
*
* This enables dynamic component instantiation from string selectors.
* Angular doesn't provide public access to this mapping, so we maintain our own.
*
* @example
* ```typescript
* GridstackComponent.addComponentToSelectorType([MyWidgetComponent]);
* ```
*/
static selectorToType: SelectorToType;
/**
* Register a list of Angular components for dynamic creation.
*
* @param typeList Array of component types to register
*
* @example
* ```typescript
* GridstackComponent.addComponentToSelectorType([
* MyWidgetComponent,
* AnotherWidgetComponent
* ]);
* ```
*/
static addComponentToSelectorType(typeList: Array<Type<Object>>): void;
/**
* Extract the selector string from an Angular component type.
*
* @param type The component type to get selector from
* @returns The component's selector string
*/
static getSelector(type: Type<Object>): string;
protected _options?: GridStackOptions;
protected _grid?: GridStack;
protected _sub: Subscription | undefined;
protected loaded?: boolean;
constructor(elementRef: ElementRef<GridCompHTMLElement>);
ngOnInit(): void;
/** wait until after all DOM is ready to init gridstack children (after angular ngFor and sub-components run first) */
ngAfterContentInit(): void;
ngOnDestroy(): void;
/**
* called when the TEMPLATE (not recommended) list of items changes - get a list of nodes and
* update the layout accordingly (which will take care of adding/removing items changed by Angular)
*/
updateAll(): void;
/** check if the grid is empty, if so show alternative content */
checkEmpty(): void;
/** get all known events as easy to use Outputs for convenience */
protected hookEvents(grid?: GridStack): void;
protected unhookEvents(grid?: GridStack): void;
static ɵfac: i0.ɵɵFactoryDeclaration<GridstackComponent, never>;
static ɵcmp: i0.ɵɵComponentDeclaration<GridstackComponent, "gridstack", never, { "options": "options"; "isEmpty": "isEmpty"; }, { "addedCB": "addedCB"; "changeCB": "changeCB"; "disableCB": "disableCB"; "dragCB": "dragCB"; "dragStartCB": "dragStartCB"; "dragStopCB": "dragStopCB"; "droppedCB": "droppedCB"; "enableCB": "enableCB"; "removedCB": "removedCB"; "resizeCB": "resizeCB"; "resizeStartCB": "resizeStartCB"; "resizeStopCB": "resizeStopCB"; }, ["gridstackItems"], ["[empty-content]", "*"], true>;
}
/**
* can be used when a new item needs to be created, which we do as a Angular component, or deleted (skip)
**/
export declare function gsCreateNgComponents(host: GridCompHTMLElement | HTMLElement, n: NgGridStackNode, add: boolean, isGrid: boolean): HTMLElement | undefined;
/**
* called for each item in the grid - check if additional information needs to be saved.
* Note: since this is options minus gridstack protected members using Utils.removeInternalForSave(),
* this typically doesn't need to do anything. However your custom Component @Input() are now supported
* using BaseWidget.serialize()
*/
export declare function gsSaveAdditionalNgInfo(n: NgGridStackNode, w: NgGridStackWidget): void;
/**
* track when widgeta re updated (rather than created) to make sure we de-serialize them as well
*/
export declare function gsUpdateNgComponents(n: NgGridStackNode): void;

View File

@@ -0,0 +1,31 @@
import * as i0 from "@angular/core";
import * as i1 from "./gridstack-item.component";
import * as i2 from "./gridstack.component";
/**
* @deprecated Use GridstackComponent and GridstackItemComponent as standalone components instead.
*
* This NgModule is provided for backward compatibility but is no longer the recommended approach.
* Import components directly in your standalone components or use the new Angular module structure.
*
* @example
* ```typescript
* // Preferred approach - standalone components
* @Component({
* selector: 'my-app',
* imports: [GridstackComponent, GridstackItemComponent],
* template: '<gridstack></gridstack>'
* })
* export class AppComponent {}
*
* // Legacy approach (deprecated)
* @NgModule({
* imports: [GridstackModule]
* })
* export class AppModule {}
* ```
*/
export declare class GridstackModule {
static ɵfac: i0.ɵɵFactoryDeclaration<GridstackModule, never>;
static ɵmod: i0.ɵɵNgModuleDeclaration<GridstackModule, never, [typeof i1.GridstackItemComponent, typeof i2.GridstackComponent], [typeof i1.GridstackItemComponent, typeof i2.GridstackComponent]>;
static ɵinj: i0.ɵɵInjectorDeclaration<GridstackModule>;
}

51
node_modules/gridstack/dist/angular/lib/types.d.ts generated vendored Normal file
View File

@@ -0,0 +1,51 @@
/**
* gridstack-item.component.ts 12.3.3
* Copyright (c) 2025 Alain Dumesny - see GridStack root license
*/
import { GridStackNode, GridStackOptions, GridStackWidget } from "gridstack";
/**
* Extended GridStackWidget interface for Angular integration.
* Adds Angular-specific properties for dynamic component creation.
*/
export interface NgGridStackWidget extends GridStackWidget {
/** Angular component selector for dynamic creation (e.g., 'my-widget') */
selector?: string;
/** Serialized data for component @Input() properties */
input?: NgCompInputs;
/** Configuration for nested sub-grids */
subGridOpts?: NgGridStackOptions;
}
/**
* Extended GridStackNode interface for Angular integration.
* Adds component selector for dynamic content creation.
*/
export interface NgGridStackNode extends GridStackNode {
/** Angular component selector for this node's content */
selector?: string;
}
/**
* Extended GridStackOptions interface for Angular integration.
* Supports Angular-specific widget definitions and nested grids.
*/
export interface NgGridStackOptions extends GridStackOptions {
/** Array of Angular widget definitions for initial grid setup */
children?: NgGridStackWidget[];
/** Configuration for nested sub-grids (Angular-aware) */
subGridOpts?: NgGridStackOptions;
}
/**
* Type for component input data serialization.
* Maps @Input() property names to their values for widget persistence.
*
* @example
* ```typescript
* const inputs: NgCompInputs = {
* title: 'My Widget',
* value: 42,
* config: { enabled: true }
* };
* ```
*/
export declare type NgCompInputs = {
[key: string]: any;
};

31
node_modules/gridstack/dist/angular/package.json generated vendored Normal file
View File

@@ -0,0 +1,31 @@
{
"name": "gridstack-angular",
"version": "12.3.3",
"peerDependencies": {
"@angular/common": ">=14",
"@angular/core": ">=14"
},
"dependencies": {
"tslib": "^2.3.0"
},
"module": "fesm2015/gridstack-angular.mjs",
"es2020": "fesm2020/gridstack-angular.mjs",
"esm2020": "esm2020/gridstack-angular.mjs",
"fesm2020": "fesm2020/gridstack-angular.mjs",
"fesm2015": "fesm2015/gridstack-angular.mjs",
"typings": "index.d.ts",
"exports": {
"./package.json": {
"default": "./package.json"
},
".": {
"types": "./index.d.ts",
"esm2020": "./esm2020/gridstack-angular.mjs",
"es2020": "./fesm2020/gridstack-angular.mjs",
"es2015": "./fesm2015/gridstack-angular.mjs",
"node": "./fesm2015/gridstack-angular.mjs",
"default": "./fesm2020/gridstack-angular.mjs"
}
},
"sideEffects": false
}

95
node_modules/gridstack/dist/angular/src/base-widget.ts generated vendored Normal file
View File

@@ -0,0 +1,95 @@
/**
* gridstack-item.component.ts 12.3.3
* Copyright (c) 2022-2024 Alain Dumesny - see GridStack root license
*/
/**
* Abstract base class that all custom widgets should extend.
*
* This class provides the interface needed for GridstackItemComponent to:
* - Serialize/deserialize widget data
* - Save/restore widget state
* - Integrate with Angular lifecycle
*
* Extend this class when creating custom widgets for dynamic grids.
*
* @example
* ```typescript
* @Component({
* selector: 'my-custom-widget',
* template: '<div>{{data}}</div>'
* })
* export class MyCustomWidget extends BaseWidget {
* @Input() data: string = '';
*
* serialize() {
* return { data: this.data };
* }
* }
* ```
*/
import { Injectable } from '@angular/core';
import { NgCompInputs, NgGridStackWidget } from './types';
/**
* Base widget class for GridStack Angular integration.
*/
@Injectable()
export abstract class BaseWidget {
/**
* Complete widget definition including position, size, and Angular-specific data.
* Populated automatically when the widget is loaded or saved.
*/
public widgetItem?: NgGridStackWidget;
/**
* Override this method to return serializable data for this widget.
*
* Return an object with properties that map to your component's @Input() fields.
* The selector is handled automatically, so only include component-specific data.
*
* @returns Object containing serializable component data
*
* @example
* ```typescript
* serialize() {
* return {
* title: this.title,
* value: this.value,
* settings: this.settings
* };
* }
* ```
*/
public serialize(): NgCompInputs | undefined { return; }
/**
* Override this method to handle widget restoration from saved data.
*
* Use this for complex initialization that goes beyond simple @Input() mapping.
* The default implementation automatically assigns input data to component properties.
*
* @param w The saved widget data including input properties
*
* @example
* ```typescript
* deserialize(w: NgGridStackWidget) {
* super.deserialize(w); // Call parent for basic setup
*
* // Custom initialization logic
* if (w.input?.complexData) {
* this.processComplexData(w.input.complexData);
* }
* }
* ```
*/
public deserialize(w: NgGridStackWidget) {
// save full description for meta data
this.widgetItem = w;
if (!w) return;
if (w.input) Object.assign(this, w.input);
}
}

View File

@@ -0,0 +1,125 @@
/**
* gridstack-item.component.ts 12.3.3
* Copyright (c) 2022-2024 Alain Dumesny - see GridStack root license
*/
import { Component, ElementRef, Input, ViewChild, ViewContainerRef, OnDestroy, ComponentRef } from '@angular/core';
import { GridItemHTMLElement, GridStackNode } from 'gridstack';
import { BaseWidget } from './base-widget';
/**
* Extended HTMLElement interface for grid items.
* Stores a back-reference to the Angular component for integration.
*/
export interface GridItemCompHTMLElement extends GridItemHTMLElement {
/** Back-reference to the Angular GridStackItem component */
_gridItemComp?: GridstackItemComponent;
}
/**
* Angular component wrapper for individual GridStack items.
*
* This component represents a single grid item and handles:
* - Dynamic content creation and management
* - Integration with parent GridStack component
* - Component lifecycle and cleanup
* - Widget options and configuration
*
* Use in combination with GridstackComponent for the parent grid.
*
* @example
* ```html
* <gridstack>
* <gridstack-item [options]="{x: 0, y: 0, w: 2, h: 1}">
* <my-widget-component></my-widget-component>
* </gridstack-item>
* </gridstack>
* ```
*/
@Component({
selector: 'gridstack-item',
template: `
<div class="grid-stack-item-content">
<!-- where dynamic items go based on component selector (recommended way), or sub-grids, etc...) -->
<ng-template #container></ng-template>
<!-- any static (defined in DOM - not recommended) content goes here -->
<ng-content></ng-content>
<!-- fallback HTML content from GridStackWidget.content if used instead (not recommended) -->
{{options.content}}
</div>`,
styles: [`
:host { display: block; }
`],
standalone: true,
// changeDetection: ChangeDetectionStrategy.OnPush, // IFF you want to optimize and control when ChangeDetection needs to happen...
})
export class GridstackItemComponent implements OnDestroy {
/**
* Container for dynamic component creation within this grid item.
* Used to append child components programmatically.
*/
@ViewChild('container', { read: ViewContainerRef, static: true}) public container?: ViewContainerRef;
/**
* Component reference for dynamic component removal.
* Used internally when this component is created dynamically.
*/
public ref: ComponentRef<GridstackItemComponent> | undefined;
/**
* Reference to child widget component for serialization.
* Used to save/restore additional data along with grid position.
*/
public childWidget: BaseWidget | undefined;
/**
* Grid item configuration options.
* Defines position, size, and behavior of this grid item.
*
* @example
* ```typescript
* itemOptions: GridStackNode = {
* x: 0, y: 0, w: 2, h: 1,
* noResize: true,
* content: 'Item content'
* };
* ```
*/
@Input() public set options(val: GridStackNode) {
const grid = this.el.gridstackNode?.grid;
if (grid) {
// already built, do an update...
grid.update(this.el, val);
} else {
// store our custom element in options so we can update it and not re-create a generic div!
this._options = {...val, el: this.el};
}
}
/** return the latest grid options (from GS once built, otherwise initial values) */
public get options(): GridStackNode {
return this.el.gridstackNode || this._options || {el: this.el};
}
protected _options?: GridStackNode;
/** return the native element that contains grid specific fields as well */
public get el(): GridItemCompHTMLElement { return this.elementRef.nativeElement; }
/** clears the initial options now that we've built */
public clearOptions() {
delete this._options;
}
constructor(protected readonly elementRef: ElementRef<GridItemCompHTMLElement>) {
this.el._gridItemComp = this;
}
public ngOnDestroy(): void {
this.clearOptions();
delete this.childWidget
delete this.el._gridItemComp;
delete this.container;
delete this.ref;
}
}

View File

@@ -0,0 +1,460 @@
/**
* gridstack.component.ts 12.3.3
* Copyright (c) 2022-2024 Alain Dumesny - see GridStack root license
*/
import {
AfterContentInit, Component, ContentChildren, ElementRef, EventEmitter, Input,
OnDestroy, OnInit, Output, QueryList, Type, ViewChild, ViewContainerRef, reflectComponentType, ComponentRef
} from '@angular/core';
import { NgIf } from '@angular/common';
import { Subscription } from 'rxjs';
import { GridHTMLElement, GridItemHTMLElement, GridStack, GridStackNode, GridStackOptions, GridStackWidget } from 'gridstack';
import { NgGridStackNode, NgGridStackWidget } from './types';
import { BaseWidget } from './base-widget';
import { GridItemCompHTMLElement, GridstackItemComponent } from './gridstack-item.component';
/**
* Event handler callback signatures for different GridStack events.
* These types define the structure of data passed to Angular event emitters.
*/
/** Callback for general events (enable, disable, etc.) */
export type eventCB = {event: Event};
/** Callback for element-specific events (resize, drag, etc.) */
export type elementCB = {event: Event, el: GridItemHTMLElement};
/** Callback for events affecting multiple nodes (change, etc.) */
export type nodesCB = {event: Event, nodes: GridStackNode[]};
/** Callback for drop events with before/after node state */
export type droppedCB = {event: Event, previousNode: GridStackNode, newNode: GridStackNode};
/**
* Extended HTMLElement interface for the grid container.
* Stores a back-reference to the Angular component for integration purposes.
*/
export interface GridCompHTMLElement extends GridHTMLElement {
/** Back-reference to the Angular GridStack component */
_gridComp?: GridstackComponent;
}
/**
* Mapping of selector strings to Angular component types.
* Used for dynamic component creation based on widget selectors.
*/
export type SelectorToType = {[key: string]: Type<Object>};
/**
* Angular component wrapper for GridStack.
*
* This component provides Angular integration for GridStack grids, handling:
* - Grid initialization and lifecycle
* - Dynamic component creation and management
* - Event binding and emission
* - Integration with Angular change detection
*
* Use in combination with GridstackItemComponent for individual grid items.
*
* @example
* ```html
* <gridstack [options]="gridOptions" (change)="onGridChange($event)">
* <div empty-content>Drag widgets here</div>
* </gridstack>
* ```
*/
@Component({
selector: 'gridstack',
template: `
<!-- content to show when when grid is empty, like instructions on how to add widgets -->
<ng-content select="[empty-content]" *ngIf="isEmpty"></ng-content>
<!-- where dynamic items go -->
<ng-template #container></ng-template>
<!-- where template items go -->
<ng-content></ng-content>
`,
styles: [`
:host { display: block; }
`],
standalone: true,
imports: [NgIf]
// changeDetection: ChangeDetectionStrategy.OnPush, // IFF you want to optimize and control when ChangeDetection needs to happen...
})
export class GridstackComponent implements OnInit, AfterContentInit, OnDestroy {
/**
* List of template-based grid items (not recommended approach).
* Used to sync between DOM and GridStack internals when items are defined in templates.
* Prefer dynamic component creation instead.
*/
@ContentChildren(GridstackItemComponent) public gridstackItems?: QueryList<GridstackItemComponent>;
/**
* Container for dynamic component creation (recommended approach).
* Used to append grid items programmatically at runtime.
*/
@ViewChild('container', { read: ViewContainerRef, static: true}) public container?: ViewContainerRef;
/**
* Grid configuration options.
* Can be set before grid initialization or updated after grid is created.
*
* @example
* ```typescript
* gridOptions: GridStackOptions = {
* column: 12,
* cellHeight: 'auto',
* animate: true
* };
* ```
*/
@Input() public set options(o: GridStackOptions) {
if (this._grid) {
this._grid.updateOptions(o);
} else {
this._options = o;
}
}
/** Get the current running grid options */
public get options(): GridStackOptions { return this._grid?.opts || this._options || {}; }
/**
* Controls whether empty content should be displayed.
* Set to true to show ng-content with 'empty-content' selector when grid has no items.
*
* @example
* ```html
* <gridstack [isEmpty]="gridItems.length === 0">
* <div empty-content>Drag widgets here to get started</div>
* </gridstack>
* ```
*/
@Input() public isEmpty?: boolean;
/**
* GridStack event emitters for Angular integration.
*
* These provide Angular-style event handling for GridStack events.
* Alternatively, use `this.grid.on('event1 event2', callback)` for multiple events.
*
* Note: 'CB' suffix prevents conflicts with native DOM events.
*
* @example
* ```html
* <gridstack (changeCB)="onGridChange($event)" (droppedCB)="onItemDropped($event)">
* </gridstack>
* ```
*/
/** Emitted when widgets are added to the grid */
@Output() public addedCB = new EventEmitter<nodesCB>();
/** Emitted when grid layout changes */
@Output() public changeCB = new EventEmitter<nodesCB>();
/** Emitted when grid is disabled */
@Output() public disableCB = new EventEmitter<eventCB>();
/** Emitted during widget drag operations */
@Output() public dragCB = new EventEmitter<elementCB>();
/** Emitted when widget drag starts */
@Output() public dragStartCB = new EventEmitter<elementCB>();
/** Emitted when widget drag stops */
@Output() public dragStopCB = new EventEmitter<elementCB>();
/** Emitted when widget is dropped */
@Output() public droppedCB = new EventEmitter<droppedCB>();
/** Emitted when grid is enabled */
@Output() public enableCB = new EventEmitter<eventCB>();
/** Emitted when widgets are removed from the grid */
@Output() public removedCB = new EventEmitter<nodesCB>();
/** Emitted during widget resize operations */
@Output() public resizeCB = new EventEmitter<elementCB>();
/** Emitted when widget resize starts */
@Output() public resizeStartCB = new EventEmitter<elementCB>();
/** Emitted when widget resize stops */
@Output() public resizeStopCB = new EventEmitter<elementCB>();
/**
* Get the native DOM element that contains grid-specific fields.
* This element has GridStack properties attached to it.
*/
public get el(): GridCompHTMLElement { return this.elementRef.nativeElement; }
/**
* Get the underlying GridStack instance.
* Use this to access GridStack API methods directly.
*
* @example
* ```typescript
* this.gridComponent.grid.addWidget({x: 0, y: 0, w: 2, h: 1});
* ```
*/
public get grid(): GridStack | undefined { return this._grid; }
/**
* Component reference for dynamic component removal.
* Used internally when this component is created dynamically.
*/
public ref: ComponentRef<GridstackComponent> | undefined;
/**
* Mapping of component selectors to their types for dynamic creation.
*
* This enables dynamic component instantiation from string selectors.
* Angular doesn't provide public access to this mapping, so we maintain our own.
*
* @example
* ```typescript
* GridstackComponent.addComponentToSelectorType([MyWidgetComponent]);
* ```
*/
public static selectorToType: SelectorToType = {};
/**
* Register a list of Angular components for dynamic creation.
*
* @param typeList Array of component types to register
*
* @example
* ```typescript
* GridstackComponent.addComponentToSelectorType([
* MyWidgetComponent,
* AnotherWidgetComponent
* ]);
* ```
*/
public static addComponentToSelectorType(typeList: Array<Type<Object>>) {
typeList.forEach(type => GridstackComponent.selectorToType[ GridstackComponent.getSelector(type) ] = type);
}
/**
* Extract the selector string from an Angular component type.
*
* @param type The component type to get selector from
* @returns The component's selector string
*/
public static getSelector(type: Type<Object>): string {
return reflectComponentType(type)!.selector;
}
protected _options?: GridStackOptions;
protected _grid?: GridStack;
protected _sub: Subscription | undefined;
protected loaded?: boolean;
constructor(protected readonly elementRef: ElementRef<GridCompHTMLElement>) {
// set globally our method to create the right widget type
if (!GridStack.addRemoveCB) {
GridStack.addRemoveCB = gsCreateNgComponents;
}
if (!GridStack.saveCB) {
GridStack.saveCB = gsSaveAdditionalNgInfo;
}
if (!GridStack.updateCB) {
GridStack.updateCB = gsUpdateNgComponents;
}
this.el._gridComp = this;
}
public ngOnInit(): void {
// init ourself before any template children are created since we track them below anyway - no need to double create+update widgets
this.loaded = !!this.options?.children?.length;
this._grid = GridStack.init(this._options, this.el);
delete this._options; // GS has it now
this.checkEmpty();
}
/** wait until after all DOM is ready to init gridstack children (after angular ngFor and sub-components run first) */
public ngAfterContentInit(): void {
// track whenever the children list changes and update the layout...
this._sub = this.gridstackItems?.changes.subscribe(() => this.updateAll());
// ...and do this once at least unless we loaded children already
if (!this.loaded) this.updateAll();
this.hookEvents(this.grid);
}
public ngOnDestroy(): void {
this.unhookEvents(this._grid);
this._sub?.unsubscribe();
this._grid?.destroy();
delete this._grid;
delete this.el._gridComp;
delete this.container;
delete this.ref;
}
/**
* called when the TEMPLATE (not recommended) list of items changes - get a list of nodes and
* update the layout accordingly (which will take care of adding/removing items changed by Angular)
*/
public updateAll() {
if (!this.grid) return;
const layout: GridStackWidget[] = [];
this.gridstackItems?.forEach(item => {
layout.push(item.options);
item.clearOptions();
});
this.grid.load(layout); // efficient that does diffs only
}
/** check if the grid is empty, if so show alternative content */
public checkEmpty() {
if (!this.grid) return;
this.isEmpty = !this.grid.engine.nodes.length;
}
/** get all known events as easy to use Outputs for convenience */
protected hookEvents(grid?: GridStack) {
if (!grid) return;
// nested grids don't have events in v12.1+ so skip
if (grid.parentGridNode) return;
grid
.on('added', (event: Event, nodes: GridStackNode[]) => {
const gridComp = (nodes[0].grid?.el as GridCompHTMLElement)._gridComp || this;
gridComp.checkEmpty();
this.addedCB.emit({event, nodes});
})
.on('change', (event: Event, nodes: GridStackNode[]) => this.changeCB.emit({event, nodes}))
.on('disable', (event: Event) => this.disableCB.emit({event}))
.on('drag', (event: Event, el: GridItemHTMLElement) => this.dragCB.emit({event, el}))
.on('dragstart', (event: Event, el: GridItemHTMLElement) => this.dragStartCB.emit({event, el}))
.on('dragstop', (event: Event, el: GridItemHTMLElement) => this.dragStopCB.emit({event, el}))
.on('dropped', (event: Event, previousNode: GridStackNode, newNode: GridStackNode) => this.droppedCB.emit({event, previousNode, newNode}))
.on('enable', (event: Event) => this.enableCB.emit({event}))
.on('removed', (event: Event, nodes: GridStackNode[]) => {
const gridComp = (nodes[0].grid?.el as GridCompHTMLElement)._gridComp || this;
gridComp.checkEmpty();
this.removedCB.emit({event, nodes});
})
.on('resize', (event: Event, el: GridItemHTMLElement) => this.resizeCB.emit({event, el}))
.on('resizestart', (event: Event, el: GridItemHTMLElement) => this.resizeStartCB.emit({event, el}))
.on('resizestop', (event: Event, el: GridItemHTMLElement) => this.resizeStopCB.emit({event, el}))
}
protected unhookEvents(grid?: GridStack) {
if (!grid) return;
// nested grids don't have events in v12.1+ so skip
if (grid.parentGridNode) return;
grid.off('added change disable drag dragstart dragstop dropped enable removed resize resizestart resizestop');
}
}
/**
* can be used when a new item needs to be created, which we do as a Angular component, or deleted (skip)
**/
export function gsCreateNgComponents(host: GridCompHTMLElement | HTMLElement, n: NgGridStackNode, add: boolean, isGrid: boolean): HTMLElement | undefined {
if (add) {
//
// create the component dynamically - see https://angular.io/docs/ts/latest/cookbook/dynamic-component-loader.html
//
if (!host) return;
if (isGrid) {
// TODO: figure out how to create ng component inside regular Div. need to access app injectors...
// if (!container) {
// const hostElement: Element = host;
// const environmentInjector: EnvironmentInjector;
// grid = createComponent(GridstackComponent, {environmentInjector, hostElement})?.instance;
// }
const gridItemComp = (host.parentElement as GridItemCompHTMLElement)?._gridItemComp;
if (!gridItemComp) return;
// check if gridItem has a child component with 'container' exposed to create under..
const container = (gridItemComp.childWidget as any)?.container || gridItemComp.container;
const gridRef = container?.createComponent(GridstackComponent);
const grid = gridRef?.instance;
if (!grid) return;
grid.ref = gridRef;
grid.options = n;
return grid.el;
} else {
const gridComp = (host as GridCompHTMLElement)._gridComp;
const gridItemRef = gridComp?.container?.createComponent(GridstackItemComponent);
const gridItem = gridItemRef?.instance;
if (!gridItem) return;
gridItem.ref = gridItemRef
// define what type of component to create as child, OR you can do it GridstackItemComponent template, but this is more generic
const selector = n.selector;
const type = selector ? GridstackComponent.selectorToType[selector] : undefined;
if (type) {
// shared code to create our selector component
const createComp = () => {
const childWidget = gridItem.container?.createComponent(type)?.instance as BaseWidget;
// if proper BaseWidget subclass, save it and load additional data
if (childWidget && typeof childWidget.serialize === 'function' && typeof childWidget.deserialize === 'function') {
gridItem.childWidget = childWidget;
childWidget.deserialize(n);
}
}
const lazyLoad = n.lazyLoad || n.grid?.opts?.lazyLoad && n.lazyLoad !== false;
if (lazyLoad) {
if (!n.visibleObservable) {
n.visibleObservable = new IntersectionObserver(([entry]) => { if (entry.isIntersecting) {
n.visibleObservable?.disconnect();
delete n.visibleObservable;
createComp();
}});
window.setTimeout(() => n.visibleObservable?.observe(gridItem.el)); // wait until callee sets position attributes
}
} else createComp();
}
return gridItem.el;
}
} else {
//
// REMOVE - have to call ComponentRef:destroy() for dynamic objects to correctly remove themselves
// Note: this will destroy all children dynamic components as well: gridItem -> childWidget
//
if (isGrid) {
const grid = (n.el as GridCompHTMLElement)?._gridComp;
if (grid?.ref) grid.ref.destroy();
else grid?.ngOnDestroy();
} else {
const gridItem = (n.el as GridItemCompHTMLElement)?._gridItemComp;
if (gridItem?.ref) gridItem.ref.destroy();
else gridItem?.ngOnDestroy();
}
}
return;
}
/**
* called for each item in the grid - check if additional information needs to be saved.
* Note: since this is options minus gridstack protected members using Utils.removeInternalForSave(),
* this typically doesn't need to do anything. However your custom Component @Input() are now supported
* using BaseWidget.serialize()
*/
export function gsSaveAdditionalNgInfo(n: NgGridStackNode, w: NgGridStackWidget) {
const gridItem = (n.el as GridItemCompHTMLElement)?._gridItemComp;
if (gridItem) {
const input = gridItem.childWidget?.serialize();
if (input) {
w.input = input;
}
return;
}
// else check if Grid
const grid = (n.el as GridCompHTMLElement)?._gridComp;
if (grid) {
//.... save any custom data
}
}
/**
* track when widgeta re updated (rather than created) to make sure we de-serialize them as well
*/
export function gsUpdateNgComponents(n: NgGridStackNode) {
const w: NgGridStackWidget = n;
const gridItem = (n.el as GridItemCompHTMLElement)?._gridItemComp;
if (gridItem?.childWidget && w.input) gridItem.childWidget.deserialize(w);
}

View File

@@ -0,0 +1,44 @@
/**
* gridstack.component.ts 12.3.3
* Copyright (c) 2022-2024 Alain Dumesny - see GridStack root license
*/
import { NgModule } from "@angular/core";
import { GridstackItemComponent } from "./gridstack-item.component";
import { GridstackComponent } from "./gridstack.component";
/**
* @deprecated Use GridstackComponent and GridstackItemComponent as standalone components instead.
*
* This NgModule is provided for backward compatibility but is no longer the recommended approach.
* Import components directly in your standalone components or use the new Angular module structure.
*
* @example
* ```typescript
* // Preferred approach - standalone components
* @Component({
* selector: 'my-app',
* imports: [GridstackComponent, GridstackItemComponent],
* template: '<gridstack></gridstack>'
* })
* export class AppComponent {}
*
* // Legacy approach (deprecated)
* @NgModule({
* imports: [GridstackModule]
* })
* export class AppModule {}
* ```
*/
@NgModule({
imports: [
GridstackItemComponent,
GridstackComponent,
],
exports: [
GridstackItemComponent,
GridstackComponent,
],
})
export class GridstackModule {}

54
node_modules/gridstack/dist/angular/src/types.ts generated vendored Normal file
View File

@@ -0,0 +1,54 @@
/**
* gridstack-item.component.ts 12.3.3
* Copyright (c) 2025 Alain Dumesny - see GridStack root license
*/
import { GridStackNode, GridStackOptions, GridStackWidget } from "gridstack";
/**
* Extended GridStackWidget interface for Angular integration.
* Adds Angular-specific properties for dynamic component creation.
*/
export interface NgGridStackWidget extends GridStackWidget {
/** Angular component selector for dynamic creation (e.g., 'my-widget') */
selector?: string;
/** Serialized data for component @Input() properties */
input?: NgCompInputs;
/** Configuration for nested sub-grids */
subGridOpts?: NgGridStackOptions;
}
/**
* Extended GridStackNode interface for Angular integration.
* Adds component selector for dynamic content creation.
*/
export interface NgGridStackNode extends GridStackNode {
/** Angular component selector for this node's content */
selector?: string;
}
/**
* Extended GridStackOptions interface for Angular integration.
* Supports Angular-specific widget definitions and nested grids.
*/
export interface NgGridStackOptions extends GridStackOptions {
/** Array of Angular widget definitions for initial grid setup */
children?: NgGridStackWidget[];
/** Configuration for nested sub-grids (Angular-aware) */
subGridOpts?: NgGridStackOptions;
}
/**
* Type for component input data serialization.
* Maps @Input() property names to their values for widget persistence.
*
* @example
* ```typescript
* const inputs: NgCompInputs = {
* title: 'My Widget',
* value: 42,
* config: { enabled: true }
* };
* ```
*/
export type NgCompInputs = {[key: string]: any};