From d31b75a2f598a31ebb42fc6e67414708ce60a5f4 Mon Sep 17 00:00:00 2001 From: Alain Dumesny Date: Sat, 9 Aug 2025 23:26:31 -0700 Subject: [PATCH] improved angular code doc --- angular/projects/lib/src/lib/base-widget.ts | 75 +++++++- .../lib/src/lib/gridstack-item.component.ts | 54 +++++- .../lib/src/lib/gridstack.component.ts | 175 +++++++++++++++--- .../projects/lib/src/lib/gridstack.module.ts | 24 ++- angular/projects/lib/src/lib/types.ts | 37 +++- 5 files changed, 323 insertions(+), 42 deletions(-) diff --git a/angular/projects/lib/src/lib/base-widget.ts b/angular/projects/lib/src/lib/base-widget.ts index d548e7911..4c797d140 100644 --- a/angular/projects/lib/src/lib/base-widget.ts +++ b/angular/projects/lib/src/lib/base-widget.ts @@ -4,27 +4,86 @@ */ /** - * Base interface that all widgets need to implement in order for GridstackItemComponent to correctly save/load/delete/.. + * 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: '
{{data}}
' + * }) + * export class MyCustomWidget extends BaseWidget { + * @Input() data: string = ''; + * + * serialize() { + * return { data: this.data }; + * } + * } + * ``` */ import { Injectable } from '@angular/core'; import { NgCompInputs, NgGridStackWidget } from './types'; - @Injectable() - export abstract class BaseWidget { +/** + * Base widget class for GridStack Angular integration. + */ +@Injectable() +export abstract class BaseWidget { - /** variable that holds the complete definition of this widgets (with selector,x,y,w,h) */ + /** + * Complete widget definition including position, size, and Angular-specific data. + * Populated automatically when the widget is loaded or saved. + */ public widgetItem?: NgGridStackWidget; /** - * REDEFINE to return an object representing the data needed to re-create yourself, other than `selector` already handled. - * This should map directly to the @Input() fields of this objects on create, so a simple apply can be used on read + * 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; } /** - * REDEFINE this if your widget needs to read from saved data and transform it to create itself - you do this for - * things that are not mapped directly into @Input() fields for example. + * 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 diff --git a/angular/projects/lib/src/lib/gridstack-item.component.ts b/angular/projects/lib/src/lib/gridstack-item.component.ts index 8308d844f..7773e71bb 100644 --- a/angular/projects/lib/src/lib/gridstack-item.component.ts +++ b/angular/projects/lib/src/lib/gridstack-item.component.ts @@ -7,13 +7,34 @@ import { Component, ElementRef, Input, ViewChild, ViewContainerRef, OnDestroy, C import { GridItemHTMLElement, GridStackNode } from 'gridstack'; import { BaseWidget } from './base-widget'; -/** store element to Ng Class pointer back */ +/** + * 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; } /** - * HTML Component Wrapper for gridstack items, in combination with GridstackComponent for parent grid + * 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 + * + * + * + * + * + * ``` */ @Component({ selector: 'gridstack-item', @@ -34,16 +55,37 @@ export interface GridItemCompHTMLElement extends GridItemHTMLElement { }) export class GridstackItemComponent implements OnDestroy { - /** container to append items dynamically */ + /** + * Container for dynamic component creation within this grid item. + * Used to append child components programmatically. + */ @ViewChild('container', { read: ViewContainerRef, static: true}) public container?: ViewContainerRef; - /** ComponentRef of ourself - used by dynamic object to correctly get removed */ + /** + * Component reference for dynamic component removal. + * Used internally when this component is created dynamically. + */ public ref: ComponentRef | undefined; - /** child component so we can save/restore additional data to be saved along */ + /** + * Reference to child widget component for serialization. + * Used to save/restore additional data along with grid position. + */ public childWidget: BaseWidget | undefined; - /** list of options for creating/updating this item */ + /** + * 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) { diff --git a/angular/projects/lib/src/lib/gridstack.component.ts b/angular/projects/lib/src/lib/gridstack.component.ts index 529da7502..636af0b4b 100644 --- a/angular/projects/lib/src/lib/gridstack.component.ts +++ b/angular/projects/lib/src/lib/gridstack.component.ts @@ -15,22 +15,55 @@ import { NgGridStackNode, NgGridStackWidget } from './types'; import { BaseWidget } from './base-widget'; import { GridItemCompHTMLElement, GridstackItemComponent } from './gridstack-item.component'; -/** events handlers emitters signature for different events */ +/** + * 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}; -/** store element to Ng Class pointer back */ +/** + * 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; } -/** selector string to runtime Type mapping */ +/** + * Mapping of selector strings to Angular component types. + * Used for dynamic component creation based on widget selectors. + */ export type SelectorToType = {[key: string]: Type}; /** - * HTML Component Wrapper for gridstack, in combination with GridstackItemComponent for the items + * 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 + * + *
Drag widgets here
+ *
+ * ``` */ @Component({ selector: 'gridstack', @@ -51,12 +84,31 @@ export type SelectorToType = {[key: string]: Type}; }) export class GridstackComponent implements OnInit, AfterContentInit, OnDestroy { - /** track list of TEMPLATE (not recommended) grid items so we can sync between DOM and GS internals */ + /** + * 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; - /** container to append items dynamically (recommended way) */ + /** + * Container for dynamic component creation (recommended approach). + * Used to append grid items programmatically at runtime. + */ @ViewChild('container', { read: ViewContainerRef, static: true}) public container?: ViewContainerRef; - /** initial options for creation of the grid */ + /** + * 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); @@ -64,51 +116,130 @@ export class GridstackComponent implements OnInit, AfterContentInit, OnDestroy { this._options = o; } } - /** return the current running options */ + /** Get the current running grid options */ public get options(): GridStackOptions { return this._grid?.opts || this._options || {}; } - /** true while ng-content with 'no-item-content' should be shown when last item is removed from a grid */ + /** + * 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 + * + *
Drag widgets here to get started
+ *
+ * ``` + */ @Input() public isEmpty?: boolean; - /** individual list of GridStackEvent callbacks handlers as output - * otherwise use this.grid.on('name1 name2 name3', callback) to handle multiple at once - * see https://github.com/gridstack/gridstack.js/blob/master/demo/events.js#L4 - * - * Note: camel casing and 'CB' added at the end to prevent @angular-eslint/no-output-native - * eg: 'change' would trigger the raw CustomEvent so use different name. + /** + * 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 + * + * + * ``` */ + + /** Emitted when widgets are added to the grid */ @Output() public addedCB = new EventEmitter(); + + /** Emitted when grid layout changes */ @Output() public changeCB = new EventEmitter(); + + /** Emitted when grid is disabled */ @Output() public disableCB = new EventEmitter(); + + /** Emitted during widget drag operations */ @Output() public dragCB = new EventEmitter(); + + /** Emitted when widget drag starts */ @Output() public dragStartCB = new EventEmitter(); + + /** Emitted when widget drag stops */ @Output() public dragStopCB = new EventEmitter(); + + /** Emitted when widget is dropped */ @Output() public droppedCB = new EventEmitter(); + + /** Emitted when grid is enabled */ @Output() public enableCB = new EventEmitter(); + + /** Emitted when widgets are removed from the grid */ @Output() public removedCB = new EventEmitter(); + + /** Emitted during widget resize operations */ @Output() public resizeCB = new EventEmitter(); + + /** Emitted when widget resize starts */ @Output() public resizeStartCB = new EventEmitter(); + + /** Emitted when widget resize stops */ @Output() public resizeStopCB = new EventEmitter(); - /** return the native element that contains grid specific fields as well */ + /** + * 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; } - /** return the GridStack class */ + /** + * 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; } - /** ComponentRef of ourself - used by dynamic object to correctly get removed */ + /** + * Component reference for dynamic component removal. + * Used internally when this component is created dynamically. + */ public ref: ComponentRef | undefined; /** - * stores the selector -> Type mapping, so we can create items dynamically from a string. - * Unfortunately Ng doesn't provide public access to that mapping. + * 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 = {}; - /** add a list of ng Component to be mapped to selector */ + /** + * 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>) { typeList.forEach(type => GridstackComponent.selectorToType[ GridstackComponent.getSelector(type) ] = type); } - /** return the ng Component selector */ + /** + * 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): string { return reflectComponentType(type)!.selector; } diff --git a/angular/projects/lib/src/lib/gridstack.module.ts b/angular/projects/lib/src/lib/gridstack.module.ts index 7aaf145dc..f95ec5268 100644 --- a/angular/projects/lib/src/lib/gridstack.module.ts +++ b/angular/projects/lib/src/lib/gridstack.module.ts @@ -8,7 +8,29 @@ import { NgModule } from "@angular/core"; import { GridstackItemComponent } from "./gridstack-item.component"; import { GridstackComponent } from "./gridstack.component"; -// @deprecated use GridstackComponent and GridstackItemComponent as standalone components +/** + * @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: '' + * }) + * export class AppComponent {} + * + * // Legacy approach (deprecated) + * @NgModule({ + * imports: [GridstackModule] + * }) + * export class AppModule {} + * ``` + */ @NgModule({ imports: [ GridstackItemComponent, diff --git a/angular/projects/lib/src/lib/types.ts b/angular/projects/lib/src/lib/types.ts index 6d840509a..72d2a8d95 100644 --- a/angular/projects/lib/src/lib/types.ts +++ b/angular/projects/lib/src/lib/types.ts @@ -5,23 +5,50 @@ import { GridStackNode, GridStackOptions, GridStackWidget } from "gridstack"; -/** extends to store Ng Component selector, instead/inAddition to content */ +/** + * Extended GridStackWidget interface for Angular integration. + * Adds Angular-specific properties for dynamic component creation. + */ export interface NgGridStackWidget extends GridStackWidget { - /** Angular tag selector for this component to create at runtime */ + /** Angular component selector for dynamic creation (e.g., 'my-widget') */ selector?: string; - /** serialized data for the component input fields */ + /** Serialized data for component @Input() properties */ input?: NgCompInputs; - /** nested grid options */ + /** 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 { - selector?: string; // component type to create as content + /** 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};