added command-form directive and logistic
This commit is contained in:
		
							parent
							
								
									8fefc1a6df
								
							
						
					
					
						commit
						860d48005e
					
				@ -1,6 +1,6 @@
 | 
			
		||||
{
 | 
			
		||||
  "name": "@openharbor/ngx-data-ui-core",
 | 
			
		||||
  "version": "18.0.0-alpha.7",
 | 
			
		||||
  "version": "18.0.0-alpha.15",
 | 
			
		||||
  "repository": "https://git.openharbor.io/Open-Harbor/ngx-data-ui",
 | 
			
		||||
  "license": "MIT",
 | 
			
		||||
  "peerDependencies": {
 | 
			
		||||
 | 
			
		||||
@ -1,4 +1,6 @@
 | 
			
		||||
import {Observable} from "rxjs";
 | 
			
		||||
import {TemplateRef} from "@angular/core";
 | 
			
		||||
import {AbstractControl, FormGroup} from "@angular/forms";
 | 
			
		||||
 | 
			
		||||
export interface IConfirmOptions {
 | 
			
		||||
  title: string;
 | 
			
		||||
@ -13,6 +15,15 @@ export interface IConfirmEvents {
 | 
			
		||||
  loading: Observable<boolean>;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export interface ICommandDirectiveService<TConfirmOptions extends IConfirmOptions = IConfirmOptions> {
 | 
			
		||||
  confirm(options?: TConfirmOptions & IConfirmEvents): Observable<boolean>;
 | 
			
		||||
export interface IConfirmForm<TControl extends FormType<TControl> = any> {
 | 
			
		||||
  formTemplate?: TemplateRef<any>;
 | 
			
		||||
  form?: FormGroup<TControl>
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export type FormType<TControl = any> = { [K in keyof TControl]: AbstractControl<any>; }
 | 
			
		||||
 | 
			
		||||
export type ConfirmOptions<TConfirmOptions extends IConfirmOptions> = TConfirmOptions & IConfirmEvents & IConfirmForm;
 | 
			
		||||
 | 
			
		||||
export interface ICommandDirectiveService<TConfirmOptions extends IConfirmOptions = IConfirmOptions> {
 | 
			
		||||
  confirm(options?: ConfirmOptions<TConfirmOptions>): Observable<boolean>;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -0,0 +1,14 @@
 | 
			
		||||
import {Directive, inject, Input, TemplateRef} from '@angular/core';
 | 
			
		||||
import {FormGroup} from "@angular/forms";
 | 
			
		||||
import {FormType} from "../abstractions/command-directive-service.abstraction";
 | 
			
		||||
 | 
			
		||||
@Directive({
 | 
			
		||||
  selector: '[duiCommandForm]',
 | 
			
		||||
  standalone: true
 | 
			
		||||
})
 | 
			
		||||
export class CommandFormDirective<TForm extends FormType<TForm>> {
 | 
			
		||||
  public readonly template = inject(TemplateRef<any>);
 | 
			
		||||
 | 
			
		||||
  @Input({ alias: 'commandForm', required: true })
 | 
			
		||||
  public form!: FormGroup<TForm>;
 | 
			
		||||
}
 | 
			
		||||
@ -1,33 +1,36 @@
 | 
			
		||||
import {Directive, EventEmitter, HostListener, Input, Output} from '@angular/core';
 | 
			
		||||
import {AfterContentInit, ContentChild, Directive, EventEmitter, HostListener, Input, Output} from '@angular/core';
 | 
			
		||||
import {IDataSource} from '@openharbor/data';
 | 
			
		||||
import {finalize} from "rxjs";
 | 
			
		||||
import {
 | 
			
		||||
  ConfirmOptions, FormType,
 | 
			
		||||
  ICommandDirectiveService,
 | 
			
		||||
  IConfirmEvents,
 | 
			
		||||
  IConfirmOptions
 | 
			
		||||
} from "../abstractions/command-directive-service.abstraction";
 | 
			
		||||
import {CommandFormDirective} from "./command-form.directive";
 | 
			
		||||
 | 
			
		||||
@Directive({
 | 
			
		||||
  selector: '[duiCommand]',
 | 
			
		||||
  standalone: true
 | 
			
		||||
})
 | 
			
		||||
export class CommandDirective<TModel extends {}, TConfirmOptions extends IConfirmOptions> {
 | 
			
		||||
export class CommandDirective<TModel extends {}, TConfirmOptions extends IConfirmOptions, TForm extends FormType<TForm> = any> implements AfterContentInit {
 | 
			
		||||
  @ContentChild(CommandFormDirective) commandFormTemplate?: CommandFormDirective<TForm>;
 | 
			
		||||
 | 
			
		||||
  @Input() confirm: boolean = false;
 | 
			
		||||
  @Input() confirmOptions?: TConfirmOptions;
 | 
			
		||||
  @Input() refresh: boolean = true;
 | 
			
		||||
  @Input() params: any;
 | 
			
		||||
  @Input() params?: any;
 | 
			
		||||
 | 
			
		||||
  @Input() dataSource!: IDataSource<TModel>;
 | 
			
		||||
  @Input() command!: string;
 | 
			
		||||
  @Input() model!: object;
 | 
			
		||||
  @Input({ required: true }) dataSource!: IDataSource<TModel>;
 | 
			
		||||
  @Input({ required: true }) command!: string;
 | 
			
		||||
  @Input() model: object = {};
 | 
			
		||||
  @Input() service?: ICommandDirectiveService;
 | 
			
		||||
 | 
			
		||||
  @Output() success: EventEmitter<any> = new EventEmitter<any>();
 | 
			
		||||
  @Output() failure: EventEmitter<any> = new EventEmitter<any>();
 | 
			
		||||
  @Output() loading: EventEmitter<boolean> = new EventEmitter<boolean>();
 | 
			
		||||
 | 
			
		||||
  constructor() {
 | 
			
		||||
 | 
			
		||||
  ngAfterContentInit(): void {
 | 
			
		||||
    console.log('init');
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @HostListener('click')
 | 
			
		||||
@ -44,14 +47,20 @@ export class CommandDirective<TModel extends {}, TConfirmOptions extends IConfir
 | 
			
		||||
        ...{
 | 
			
		||||
          success: this.success.asObservable(),
 | 
			
		||||
          failure: this.failure.asObservable(),
 | 
			
		||||
          loading: this.loading.asObservable()
 | 
			
		||||
          loading: this.loading.asObservable(),
 | 
			
		||||
        },
 | 
			
		||||
        ...{
 | 
			
		||||
          formTemplate: this.commandFormTemplate?.template,
 | 
			
		||||
          form: this.commandFormTemplate?.form
 | 
			
		||||
        }
 | 
			
		||||
      } as TConfirmOptions & IConfirmEvents;
 | 
			
		||||
      } as ConfirmOptions<TConfirmOptions>;
 | 
			
		||||
 | 
			
		||||
      this.service.confirm(options)
 | 
			
		||||
        .subscribe(result => {
 | 
			
		||||
          const model = { ...this.model, ...options.form?.getRawValue() };
 | 
			
		||||
          console.log(options.form?.getRawValue());
 | 
			
		||||
          if (result)
 | 
			
		||||
            this.executeCommand();
 | 
			
		||||
            this.executeCommand(model);
 | 
			
		||||
        });
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
@ -59,10 +68,10 @@ export class CommandDirective<TModel extends {}, TConfirmOptions extends IConfir
 | 
			
		||||
    this.executeCommand();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private executeCommand() {
 | 
			
		||||
  private executeCommand(model?: any) {
 | 
			
		||||
    this.dataSource.resolveCommandModelByName<TModel>({
 | 
			
		||||
      command: this.command,
 | 
			
		||||
      model: this.model,
 | 
			
		||||
      model: model,
 | 
			
		||||
      params: this.params
 | 
			
		||||
    })
 | 
			
		||||
      .subscribe({ next: commandModel => {
 | 
			
		||||
 | 
			
		||||
@ -0,0 +1 @@
 | 
			
		||||
<p>data-grid works!</p>
 | 
			
		||||
@ -0,0 +1,23 @@
 | 
			
		||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
 | 
			
		||||
 | 
			
		||||
import { DataGridComponent } from './data-grid.component';
 | 
			
		||||
 | 
			
		||||
describe('DataGridComponent', () => {
 | 
			
		||||
  let component: DataGridComponent;
 | 
			
		||||
  let fixture: ComponentFixture<DataGridComponent>;
 | 
			
		||||
 | 
			
		||||
  beforeEach(async () => {
 | 
			
		||||
    await TestBed.configureTestingModule({
 | 
			
		||||
      imports: [DataGridComponent]
 | 
			
		||||
    })
 | 
			
		||||
    .compileComponents();
 | 
			
		||||
 | 
			
		||||
    fixture = TestBed.createComponent(DataGridComponent);
 | 
			
		||||
    component = fixture.componentInstance;
 | 
			
		||||
    fixture.detectChanges();
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  it('should create', () => {
 | 
			
		||||
    expect(component).toBeTruthy();
 | 
			
		||||
  });
 | 
			
		||||
});
 | 
			
		||||
@ -0,0 +1,13 @@
 | 
			
		||||
import { Component } from '@angular/core';
 | 
			
		||||
 | 
			
		||||
@Component({
 | 
			
		||||
  selector: 'dui-data-grid',
 | 
			
		||||
  standalone: true,
 | 
			
		||||
  imports: [],
 | 
			
		||||
  templateUrl: './data-grid.component.html',
 | 
			
		||||
  styleUrl: './data-grid.component.css'
 | 
			
		||||
})
 | 
			
		||||
export class DataGridComponent<TModel> {
 | 
			
		||||
 | 
			
		||||
  @Input() dataSource: IDataSource<TModel>;
 | 
			
		||||
}
 | 
			
		||||
@ -4,3 +4,4 @@
 | 
			
		||||
 | 
			
		||||
export * from './lib/command/directives/command.directive';
 | 
			
		||||
export * from './lib/command/abstractions/command-directive-service.abstraction';
 | 
			
		||||
export * from './lib/command/directives/command-form.directive';
 | 
			
		||||
 | 
			
		||||
@ -1,6 +1,6 @@
 | 
			
		||||
{
 | 
			
		||||
  "name": "@openharbor/ngx-data-ui-md",
 | 
			
		||||
  "version": "18.0.0-alpha.8",
 | 
			
		||||
  "version": "18.0.0-alpha.24",
 | 
			
		||||
  "repository": "https://git.openharbor.io/Open-Harbor/ngx-data-ui",
 | 
			
		||||
  "license": "MIT",
 | 
			
		||||
  "peerDependencies": {
 | 
			
		||||
 | 
			
		||||
@ -1,5 +1,5 @@
 | 
			
		||||
import {inject, Injectable} from '@angular/core';
 | 
			
		||||
import {ICommandDirectiveService, IConfirmEvents, IConfirmOptions} from "@openharbor/ngx-data-ui-core";
 | 
			
		||||
import {ConfirmOptions, ICommandDirectiveService, IConfirmOptions} from "@openharbor/ngx-data-ui-core";
 | 
			
		||||
import {Observable} from 'rxjs';
 | 
			
		||||
import {MatDialog, MatDialogConfig} from "@angular/material/dialog";
 | 
			
		||||
import {
 | 
			
		||||
@ -17,7 +17,7 @@ export interface IMDCommandDirectiveServiceOptions extends IConfirmOptions {
 | 
			
		||||
export class CommandDirectiveService implements ICommandDirectiveService<IMDCommandDirectiveServiceOptions> {
 | 
			
		||||
  readonly dialog = inject(MatDialog);
 | 
			
		||||
 | 
			
		||||
  confirm(options: IMDCommandDirectiveServiceOptions & IConfirmEvents): Observable<boolean> {
 | 
			
		||||
  confirm(options: ConfirmOptions<IMDCommandDirectiveServiceOptions>): Observable<boolean> {
 | 
			
		||||
    const defaultOptions: Partial<IMDCommandDirectiveServiceOptions> = {
 | 
			
		||||
      confirmText: 'Confirm',
 | 
			
		||||
      cancelText: 'Cancel'
 | 
			
		||||
@ -39,7 +39,9 @@ export class CommandDirectiveService implements ICommandDirectiveService<IMDComm
 | 
			
		||||
            cancelText: finalOptions.cancelText,
 | 
			
		||||
            success: options.success,
 | 
			
		||||
            loading: options.loading,
 | 
			
		||||
            failure: options.failure
 | 
			
		||||
            failure: options.failure,
 | 
			
		||||
            formTemplate: options.formTemplate,
 | 
			
		||||
            form: options.form
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
      };
 | 
			
		||||
 | 
			
		||||
@ -1,6 +1,11 @@
 | 
			
		||||
<h2 mat-dialog-title>{{ title }}</h2>
 | 
			
		||||
<mat-dialog-content>
 | 
			
		||||
  {{ message }}
 | 
			
		||||
 | 
			
		||||
  @if (formTemplate) {
 | 
			
		||||
    <!-- [ngTemplateOutletContext]="{ $implicit: model, loading: loading, dataSource: dataSource }" -->
 | 
			
		||||
    <ng-container [ngTemplateOutlet]="formTemplate"></ng-container>
 | 
			
		||||
  }
 | 
			
		||||
</mat-dialog-content>
 | 
			
		||||
<mat-dialog-actions>
 | 
			
		||||
  <button mat-button mat-dialog-close cdkFocusInitial [disabled]="isLoading">{{ cancelText ?? 'Cancel' }}</button>
 | 
			
		||||
 | 
			
		||||
@ -1,17 +1,20 @@
 | 
			
		||||
import {Component, EventEmitter, inject, OnDestroy, OnInit} from '@angular/core';
 | 
			
		||||
import {Component, EventEmitter, inject, OnDestroy, OnInit, TemplateRef} from '@angular/core';
 | 
			
		||||
import {
 | 
			
		||||
  MAT_DIALOG_DATA,
 | 
			
		||||
  MatDialogActions,
 | 
			
		||||
  MatDialogClose,
 | 
			
		||||
  MatDialogContent, MatDialogRef,
 | 
			
		||||
  MatDialogContent,
 | 
			
		||||
  MatDialogRef,
 | 
			
		||||
  MatDialogTitle
 | 
			
		||||
} from "@angular/material/dialog";
 | 
			
		||||
import {MatButton} from "@angular/material/button";
 | 
			
		||||
import {AsyncPipe} from "@angular/common";
 | 
			
		||||
import {AsyncPipe, NgTemplateOutlet} from "@angular/common";
 | 
			
		||||
import {MatProgressSpinner} from "@angular/material/progress-spinner";
 | 
			
		||||
import {Observable, Subscription} from "rxjs";
 | 
			
		||||
import {FormGroup} from "@angular/forms";
 | 
			
		||||
import {FormType} from "@openharbor/ngx-data-ui-core";
 | 
			
		||||
 | 
			
		||||
export interface IConfirmDialogDefaultOptions {
 | 
			
		||||
export interface IConfirmDialogDefaultOptions<TControl extends FormType<TControl> = any> {
 | 
			
		||||
  title: string;
 | 
			
		||||
  message: string;
 | 
			
		||||
  confirmText?: string;
 | 
			
		||||
@ -19,10 +22,11 @@ export interface IConfirmDialogDefaultOptions {
 | 
			
		||||
  success: Observable<any>;
 | 
			
		||||
  failure: Observable<any>;
 | 
			
		||||
  loading: Observable<boolean>;
 | 
			
		||||
  formTemplate?: TemplateRef<any>;
 | 
			
		||||
  form?: FormGroup<TControl>;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@Component({
 | 
			
		||||
  selector: 'mdx-confirm-dialog-default',
 | 
			
		||||
  standalone: true,
 | 
			
		||||
  imports: [
 | 
			
		||||
    MatDialogContent,
 | 
			
		||||
@ -31,13 +35,14 @@ export interface IConfirmDialogDefaultOptions {
 | 
			
		||||
    MatDialogClose,
 | 
			
		||||
    MatDialogTitle,
 | 
			
		||||
    AsyncPipe,
 | 
			
		||||
    MatProgressSpinner
 | 
			
		||||
    MatProgressSpinner,
 | 
			
		||||
    NgTemplateOutlet
 | 
			
		||||
  ],
 | 
			
		||||
  templateUrl: './confirm-dialog-default.component.html',
 | 
			
		||||
  styleUrl: './confirm-dialog-default.component.css'
 | 
			
		||||
})
 | 
			
		||||
export class ConfirmDialogDefaultComponent implements OnInit, OnDestroy {
 | 
			
		||||
  readonly data = inject<IConfirmDialogDefaultOptions>(MAT_DIALOG_DATA);
 | 
			
		||||
export class ConfirmDialogDefaultComponent<TControl extends FormType<TControl> = any> implements OnInit, OnDestroy {
 | 
			
		||||
  readonly data = inject<IConfirmDialogDefaultOptions<TControl>>(MAT_DIALOG_DATA);
 | 
			
		||||
  readonly ref = inject(MatDialogRef<ConfirmDialogDefaultComponent>);
 | 
			
		||||
 | 
			
		||||
  readonly title: string = this.data.title;
 | 
			
		||||
@ -46,6 +51,9 @@ export class ConfirmDialogDefaultComponent implements OnInit, OnDestroy {
 | 
			
		||||
  readonly cancelText?: string = this.data.cancelText;
 | 
			
		||||
  readonly $loading: Observable<boolean> = this.data.loading;
 | 
			
		||||
  readonly success: Observable<any> = this.data.success;
 | 
			
		||||
  readonly formTemplate?: TemplateRef<any> = this.data.formTemplate;
 | 
			
		||||
  readonly form?: FormGroup<TControl>;
 | 
			
		||||
 | 
			
		||||
  isLoading = false;
 | 
			
		||||
  // todo: error messaging?
 | 
			
		||||
 | 
			
		||||
@ -75,6 +83,18 @@ export class ConfirmDialogDefaultComponent implements OnInit, OnDestroy {
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  onConfirmed() {
 | 
			
		||||
    if (!this.formTemplate || !this.form) {
 | 
			
		||||
      this._onConfirm.emit();
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    this.form.clearValidators();
 | 
			
		||||
 | 
			
		||||
    if (!this.form.valid) {
 | 
			
		||||
      this.form.markAllAsTouched();
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    this._onConfirm.emit();
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -0,0 +1,50 @@
 | 
			
		||||
<div class="mat-elevation-z8">
 | 
			
		||||
  @if ($data) {
 | 
			
		||||
    <table mat-table [dataSource]="$data" matSort
 | 
			
		||||
       (matSortChange)="onSortChanged($event)">
 | 
			
		||||
      @for (column of _options.columns; track column.column) {
 | 
			
		||||
        <ng-container [matColumnDef]="column.column">
 | 
			
		||||
          <th mat-header-cell *matHeaderCellDef
 | 
			
		||||
              [mat-sort-header]="column.sorting && column.sorting.path ? column.sorting.path : column.column"
 | 
			
		||||
              [disabled]="!(column.sorting?.enable)"
 | 
			
		||||
              [sortActionDescription]="column.sorting && column.sorting.description ? (column.sorting.description(column.column) | async) ?? '' : 'Sort by ' + column.column">
 | 
			
		||||
            @if (column.title) {
 | 
			
		||||
              {{ column.title(column.column) | async }}
 | 
			
		||||
            }
 | 
			
		||||
            @else {
 | 
			
		||||
              {{ column.column | titlecase }}
 | 
			
		||||
            }
 | 
			
		||||
          </th>
 | 
			
		||||
 | 
			
		||||
          <td mat-cell *matCellDef="let element">
 | 
			
		||||
            @if (column.value) {
 | 
			
		||||
              {{ column.value(element[column.column]) | async }}
 | 
			
		||||
            }
 | 
			
		||||
            @else {
 | 
			
		||||
              {{ element[column.column] }}
 | 
			
		||||
            }
 | 
			
		||||
          </td>
 | 
			
		||||
        </ng-container>
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      <tr mat-header-row *matHeaderRowDef="getColumns()"></tr>
 | 
			
		||||
      <tr mat-row *matRowDef="let row; columns: getColumns();"></tr>
 | 
			
		||||
    </table>
 | 
			
		||||
 | 
			
		||||
    @if(_options.pagination.enable) {
 | 
			
		||||
      <mat-paginator
 | 
			
		||||
        [disabled]="isLoading"
 | 
			
		||||
        [hidePageSize]="_options.pagination.hidePageSize"
 | 
			
		||||
        [pageSizeOptions]="_options.pagination.pageSizeOptions"
 | 
			
		||||
        (page)="onPaginationEvent($event)"
 | 
			
		||||
        [length]="totalRecords"
 | 
			
		||||
        showFirstLastButtons
 | 
			
		||||
        aria-label="Select page of periodic elements">
 | 
			
		||||
      </mat-paginator>
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @if (isLoading) {
 | 
			
		||||
      <mat-progress-bar mode="query"></mat-progress-bar>
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
</div>
 | 
			
		||||
@ -0,0 +1,23 @@
 | 
			
		||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
 | 
			
		||||
 | 
			
		||||
import { DataGridComponent } from './data-grid.component';
 | 
			
		||||
 | 
			
		||||
describe('DataGridComponent', () => {
 | 
			
		||||
  let component: DataGridComponent;
 | 
			
		||||
  let fixture: ComponentFixture<DataGridComponent>;
 | 
			
		||||
 | 
			
		||||
  beforeEach(async () => {
 | 
			
		||||
    await TestBed.configureTestingModule({
 | 
			
		||||
      imports: [DataGridComponent]
 | 
			
		||||
    })
 | 
			
		||||
    .compileComponents();
 | 
			
		||||
 | 
			
		||||
    fixture = TestBed.createComponent(DataGridComponent);
 | 
			
		||||
    component = fixture.componentInstance;
 | 
			
		||||
    fixture.detectChanges();
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  it('should create', () => {
 | 
			
		||||
    expect(component).toBeTruthy();
 | 
			
		||||
  });
 | 
			
		||||
});
 | 
			
		||||
@ -0,0 +1,161 @@
 | 
			
		||||
import {Component, Input, OnDestroy, OnInit} from '@angular/core';
 | 
			
		||||
import {IDataSource} from "@openharbor/data";
 | 
			
		||||
import {
 | 
			
		||||
  MatCell,
 | 
			
		||||
  MatCellDef,
 | 
			
		||||
  MatColumnDef,
 | 
			
		||||
  MatHeaderCell,
 | 
			
		||||
  MatHeaderCellDef,
 | 
			
		||||
  MatHeaderRow,
 | 
			
		||||
  MatHeaderRowDef,
 | 
			
		||||
  MatRow,
 | 
			
		||||
  MatRowDef,
 | 
			
		||||
  MatTable
 | 
			
		||||
} from "@angular/material/table";
 | 
			
		||||
import {MatPaginator, PageEvent} from "@angular/material/paginator";
 | 
			
		||||
import {map, Observable, Subscription, tap} from "rxjs";
 | 
			
		||||
import {MatProgressBar} from "@angular/material/progress-bar";
 | 
			
		||||
import {AsyncPipe, JsonPipe, TitleCasePipe} from "@angular/common";
 | 
			
		||||
import {MatSort, MatSortHeader, Sort} from "@angular/material/sort";
 | 
			
		||||
 | 
			
		||||
export interface IDataGridOptions<TModel extends object> {
 | 
			
		||||
  columns: IColumnDefinition<TModel, Extract<keyof TModel, string>>[];
 | 
			
		||||
  actions?: [];
 | 
			
		||||
  pagination: {
 | 
			
		||||
    enable: boolean;
 | 
			
		||||
    pageSize: number;
 | 
			
		||||
    hidePageSize: boolean;
 | 
			
		||||
    pageSizeOptions: number[];
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export interface IColumnDefinition<TModel extends object, TColumn extends Extract<keyof TModel, string>> {
 | 
			
		||||
  column: TColumn;
 | 
			
		||||
  sorting?: {
 | 
			
		||||
    enable: boolean;
 | 
			
		||||
    description?: (element: TColumn) => Observable<string>;
 | 
			
		||||
    path?: string;
 | 
			
		||||
  },
 | 
			
		||||
  title?: (element: TColumn) => Observable<string>;
 | 
			
		||||
  value?: (element: TModel[TColumn]) => Observable<string|number|boolean>;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export interface IRowAction<TModel extends object> {
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@Component({
 | 
			
		||||
  selector: 'mdx-data-grid',
 | 
			
		||||
  standalone: true,
 | 
			
		||||
  imports: [
 | 
			
		||||
    MatTable,
 | 
			
		||||
    MatColumnDef,
 | 
			
		||||
    MatHeaderCell,
 | 
			
		||||
    MatCell,
 | 
			
		||||
    MatPaginator,
 | 
			
		||||
    MatHeaderRow,
 | 
			
		||||
    MatHeaderRowDef,
 | 
			
		||||
    MatRow,
 | 
			
		||||
    MatRowDef,
 | 
			
		||||
    MatProgressBar,
 | 
			
		||||
    MatHeaderCellDef,
 | 
			
		||||
    MatCellDef,
 | 
			
		||||
    TitleCasePipe,
 | 
			
		||||
    JsonPipe,
 | 
			
		||||
    AsyncPipe,
 | 
			
		||||
    MatSort,
 | 
			
		||||
    MatSortHeader
 | 
			
		||||
  ],
 | 
			
		||||
  templateUrl: './data-grid.component.html',
 | 
			
		||||
  styleUrl: './data-grid.component.css'
 | 
			
		||||
})
 | 
			
		||||
export class DataGridComponent<TModel extends object> implements OnInit, OnDestroy {
 | 
			
		||||
  @Input({ required: true }) dataSource!: IDataSource<TModel>;
 | 
			
		||||
  @Input() options?: Partial<IDataGridOptions<TModel>>;
 | 
			
		||||
 | 
			
		||||
  private subscriptions: Subscription[] = [];
 | 
			
		||||
 | 
			
		||||
  $data?: Observable<TModel[]>;
 | 
			
		||||
  totalRecords: number = 0;
 | 
			
		||||
  isLoading: boolean = false;
 | 
			
		||||
 | 
			
		||||
  _options: IDataGridOptions<TModel> = {
 | 
			
		||||
    columns: [],
 | 
			
		||||
    pagination: {
 | 
			
		||||
      enable: true,
 | 
			
		||||
      hidePageSize: false,
 | 
			
		||||
      pageSizeOptions: [10, 25, 50, 100],
 | 
			
		||||
      pageSize: 25
 | 
			
		||||
    }
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  getColumns() {
 | 
			
		||||
    return this._options.columns.map(options => {
 | 
			
		||||
      return options.column;
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  ngOnInit() {
 | 
			
		||||
    this._options = {
 | 
			
		||||
      ...this._options,
 | 
			
		||||
      ...this.options
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    this.$data = this.dataSource.data$
 | 
			
		||||
      .pipe(
 | 
			
		||||
        tap((result => {
 | 
			
		||||
          this.totalRecords = result?.totalRecords ?? 0;
 | 
			
		||||
        })),
 | 
			
		||||
        map((result) => {
 | 
			
		||||
          return result?.data ?? [];
 | 
			
		||||
        })
 | 
			
		||||
      );
 | 
			
		||||
 | 
			
		||||
    this.subscriptions.push(this.dataSource.loading$
 | 
			
		||||
      .subscribe(loading => this.isLoading = loading)
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  ngOnDestroy() {
 | 
			
		||||
    for (let subscription of this.subscriptions)
 | 
			
		||||
      subscription.unsubscribe();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  onPaginationEvent(ev: PageEvent) {
 | 
			
		||||
    this.setPageSize(ev.pageSize);
 | 
			
		||||
    this.setPage(ev.pageIndex);
 | 
			
		||||
    this.refresh();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  setPage(index: number) {
 | 
			
		||||
    this.dataSource.page = index + 1;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  setPageSize(size: number) {
 | 
			
		||||
    this._options.pagination.pageSize = size;
 | 
			
		||||
    this.dataSource.pageSize = size;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  refresh() {
 | 
			
		||||
    this.dataSource.refresh();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  onSortChanged(state: Sort) {
 | 
			
		||||
    if (state.direction === '') {
 | 
			
		||||
      this.dataSource.sorts = this.dataSource.sorts.filter(sort => sort.path !== state.active);
 | 
			
		||||
    } else {
 | 
			
		||||
      let field = this.dataSource.sorts.find(field => field.path === state.active);
 | 
			
		||||
      if (field === undefined) {
 | 
			
		||||
        field = {
 | 
			
		||||
          path: state.active
 | 
			
		||||
        };
 | 
			
		||||
        this.dataSource.sorts.push(field);
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
      field.ascending = state.direction === 'asc';
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    this.refresh();
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
@ -4,3 +4,4 @@
 | 
			
		||||
 | 
			
		||||
export * from './lib/command/services/command-directive.service';
 | 
			
		||||
export * from './lib/confirm-dialog/confirm-dialog-default/confirm-dialog-default.component';
 | 
			
		||||
export * from './lib/data-grid/data-grid/data-grid.component';
 | 
			
		||||
 | 
			
		||||
		Loading…
	
		Reference in New Issue
	
	Block a user