fix bugs & mostly completed confirm command

This commit is contained in:
Mathias Beaulieu-Duncan 2024-09-03 04:27:07 -04:00
parent 79d474f9f5
commit 8fefc1a6df
Signed by: mathias
GPG Key ID: 1C16CF05BAF9162D
13 changed files with 130 additions and 45 deletions

View File

@ -3,8 +3,10 @@
"version": "0.1.0", "version": "0.1.0",
"scripts": { "scripts": {
"up-md": "ng serve md-demo", "up-md": "ng serve md-demo",
"build-core": "ng build core --configuration production",
"publish-core": "npm publish dist/openharbor/core --access public --tag next",
"build-md": "ng build md-ui --configuration production", "build-md": "ng build md-ui --configuration production",
"publish-md": "npm publish md-ui --access-public" "publish-md": "npm publish dist/openharbor/md-ui --access public --tag next"
}, },
"private": true, "private": true,
"dependencies": { "dependencies": {
@ -18,7 +20,8 @@
"@angular/platform-browser": "^18.0.0", "@angular/platform-browser": "^18.0.0",
"@angular/platform-browser-dynamic": "^18.0.0", "@angular/platform-browser-dynamic": "^18.0.0",
"@angular/router": "^18.0.0", "@angular/router": "^18.0.0",
"@openharbor/data": "^1.0.0-alpha.1", "@openharbor/data": "1.0.0-alpha.4",
"@openharbor/ngx-data-ui-core": "^18.0.0-alpha.1",
"rxjs": "~7.8.0", "rxjs": "~7.8.0",
"tslib": "^2.3.0", "tslib": "^2.3.0",
"zone.js": "~0.14.3" "zone.js": "~0.14.3"

View File

@ -1,6 +1,6 @@
{ {
"$schema": "../../node_modules/ng-packagr/ng-package.schema.json", "$schema": "../../node_modules/ng-packagr/ng-package.schema.json",
"dest": "../../dist/core", "dest": "../../dist/openharbor/core",
"lib": { "lib": {
"entryFile": "src/public-api.ts" "entryFile": "src/public-api.ts"
} }

View File

@ -1,6 +1,8 @@
{ {
"name": "@openharbor/ngx-data-ui-core", "name": "@openharbor/ngx-data-ui-core",
"version": "18.0.0-alpha.1", "version": "18.0.0-alpha.7",
"repository": "https://git.openharbor.io/Open-Harbor/ngx-data-ui",
"license": "MIT",
"peerDependencies": { "peerDependencies": {
"@angular/common": "^18.2.0", "@angular/common": "^18.2.0",
"@angular/core": "^18.2.0", "@angular/core": "^18.2.0",

View File

@ -7,6 +7,12 @@ export interface IConfirmOptions {
cancelText?: string; cancelText?: string;
} }
export abstract class ICommandDirectiveService<TConfirmOptions extends IConfirmOptions = IConfirmOptions> { export interface IConfirmEvents {
abstract confirm(options: TConfirmOptions): Observable<boolean>; success: Observable<any>;
failure: Observable<any>;
loading: Observable<boolean>;
}
export interface ICommandDirectiveService<TConfirmOptions extends IConfirmOptions = IConfirmOptions> {
confirm(options?: TConfirmOptions & IConfirmEvents): Observable<boolean>;
} }

View File

@ -1,28 +1,32 @@
import {Directive, EventEmitter, HostListener, Inject, Input, Optional, Output} from '@angular/core'; import {Directive, EventEmitter, HostListener, Input, Output} from '@angular/core';
import {IDataSource} from '@poweredsoft/data'; import {IDataSource} from '@openharbor/data';
import {finalize} from "rxjs"; import {finalize} from "rxjs";
import {ICommandDirectiveService, IConfirmOptions} from "../abstractions/command-directive-service.abstraction"; import {
ICommandDirectiveService,
IConfirmEvents,
IConfirmOptions
} from "../abstractions/command-directive-service.abstraction";
@Directive({ @Directive({
selector: '[duiCommand]', selector: '[duiCommand]',
standalone: true standalone: true
}) })
export class CommandDirective<TModel extends {}, TConfirmOptions extends IConfirmOptions> { export class CommandDirective<TModel extends {}, TConfirmOptions extends IConfirmOptions> {
@Input() confirm: boolean = false; @Input() confirm: boolean = false;
@Input() confirmOptions?: TConfirmOptions; @Input() confirmOptions?: TConfirmOptions;
@Input() refreshOnSuccess: boolean = false; @Input() refresh: boolean = true;
@Input() params: any; @Input() params: any;
@Input() dataSource!: IDataSource<TModel>; @Input() dataSource!: IDataSource<TModel>;
@Input() command!: string; @Input() command!: string;
@Input() model!: TModel; @Input() model!: object;
@Input() service?: ICommandDirectiveService;
@Output() success: EventEmitter<any> = new EventEmitter<any>(); @Output() success: EventEmitter<any> = new EventEmitter<any>();
@Output() failure: EventEmitter<any> = new EventEmitter<any>(); @Output() failure: EventEmitter<any> = new EventEmitter<any>();
@Output() loading: EventEmitter<boolean> = new EventEmitter<boolean>(); @Output() loading: EventEmitter<boolean> = new EventEmitter<boolean>();
constructor(@Optional() private service?: ICommandDirectiveService<TConfirmOptions>) { constructor() {
} }
@ -30,12 +34,21 @@ export class CommandDirective<TModel extends {}, TConfirmOptions extends IConfir
handleClick() { handleClick() {
if (this.confirm) { if (this.confirm) {
if (!this.service) { if (!this.service) {
const error = new Error(`NullInjectorError: No implementation provider for CommandDirectiveService Abstraction!`); const error = new Error(`No service provided to directive for CommandDirectiveService!`);
error.name = 'NullInjectorError'; error.name = 'NullInjectorError';
throw error; throw error;
} }
this.service.confirm(this.confirmOptions) const options = {
...this.confirmOptions,
...{
success: this.success.asObservable(),
failure: this.failure.asObservable(),
loading: this.loading.asObservable()
}
} as TConfirmOptions & IConfirmEvents;
this.service.confirm(options)
.subscribe(result => { .subscribe(result => {
if (result) if (result)
this.executeCommand(); this.executeCommand();
@ -61,7 +74,7 @@ export class CommandDirective<TModel extends {}, TConfirmOptions extends IConfir
.subscribe({ .subscribe({
next: commandResult => next: commandResult =>
{ {
if (this.refreshOnSuccess) if (this.refresh)
this.dataSource.refresh(); this.dataSource.refresh();
this.success.emit(commandResult); this.success.emit(commandResult);

View File

@ -1,6 +1,6 @@
{ {
"$schema": "../../node_modules/ng-packagr/ng-package.schema.json", "$schema": "../../node_modules/ng-packagr/ng-package.schema.json",
"dest": "../../dist/md-ui", "dest": "../../dist/openharbor/md-ui",
"lib": { "lib": {
"entryFile": "src/public-api.ts" "entryFile": "src/public-api.ts"
} }

View File

@ -1,6 +1,8 @@
{ {
"name": "@openharbor/ngx-data-ui-md", "name": "@openharbor/ngx-data-ui-md",
"version": "18.0.0-alpha.1", "version": "18.0.0-alpha.8",
"repository": "https://git.openharbor.io/Open-Harbor/ngx-data-ui",
"license": "MIT",
"peerDependencies": { "peerDependencies": {
"@angular/common": "^18.0.0", "@angular/common": "^18.0.0",
"@angular/core": "^18.0.0", "@angular/core": "^18.0.0",

View File

@ -1,5 +1,5 @@
import {inject, Injectable} from '@angular/core'; import {inject, Injectable} from '@angular/core';
import {ICommandDirectiveService, IConfirmOptions} from "@openharbor/ngx-data-ui-core"; import {ICommandDirectiveService, IConfirmEvents, IConfirmOptions} from "@openharbor/ngx-data-ui-core";
import {Observable} from 'rxjs'; import {Observable} from 'rxjs';
import {MatDialog, MatDialogConfig} from "@angular/material/dialog"; import {MatDialog, MatDialogConfig} from "@angular/material/dialog";
import { import {
@ -12,14 +12,13 @@ export interface IMDCommandDirectiveServiceOptions extends IConfirmOptions {
} }
@Injectable({ @Injectable({
providedIn: 'root', providedIn: 'root'
useExisting: ICommandDirectiveService
}) })
export class CommandDirectiveService extends ICommandDirectiveService<IMDCommandDirectiveServiceOptions> { export class CommandDirectiveService implements ICommandDirectiveService<IMDCommandDirectiveServiceOptions> {
readonly dialog = inject(MatDialog); readonly dialog = inject(MatDialog);
confirm(options: IMDCommandDirectiveServiceOptions): Observable<boolean> { confirm(options: IMDCommandDirectiveServiceOptions & IConfirmEvents): Observable<boolean> {
const defaultOptions: Partial<IConfirmOptions> = { const defaultOptions: Partial<IMDCommandDirectiveServiceOptions> = {
confirmText: 'Confirm', confirmText: 'Confirm',
cancelText: 'Cancel' cancelText: 'Cancel'
}; };
@ -38,13 +37,23 @@ export class CommandDirectiveService extends ICommandDirectiveService<IMDCommand
message: finalOptions.message, message: finalOptions.message,
confirmText: finalOptions.confirmText, confirmText: finalOptions.confirmText,
cancelText: finalOptions.cancelText, cancelText: finalOptions.cancelText,
success: options.success,
loading: options.loading,
failure: options.failure
} }
} }
}; };
this.dialog.open<ConfirmDialogDefaultComponent, IConfirmDialogDefaultOptions>(ConfirmDialogDefaultComponent, dialogOptions); const dialogRef = this.dialog.open<ConfirmDialogDefaultComponent, IConfirmDialogDefaultOptions>(ConfirmDialogDefaultComponent, dialogOptions);
const onConfirmSub = dialogRef.componentInstance.onConfirm
.subscribe(_ => {
subscriber.next(true);
});
subscriber.next(true); dialogRef.afterClosed()
.subscribe(_ => {
onConfirmSub.unsubscribe();
});
}); });
} }
} }

View File

@ -3,6 +3,12 @@
{{ message }} {{ message }}
</mat-dialog-content> </mat-dialog-content>
<mat-dialog-actions> <mat-dialog-actions>
<button mat-button mat-dialog-close>{{ cancelText ?? 'Cancel' }}</button> <button mat-button mat-dialog-close cdkFocusInitial [disabled]="isLoading">{{ cancelText ?? 'Cancel' }}</button>
<button mat-button mat-dialog-close cdkFocusInitial>{{ confirmText ?? 'Confirm' }}</button> <button mat-button (click)="onConfirmed()" [disabled]="isLoading">
@if(isLoading) {
<mat-spinner diameter="18"></mat-spinner>
} @else {
{{ confirmText ?? 'Confirm' }}
}
</button>
</mat-dialog-actions> </mat-dialog-actions>

View File

@ -1,18 +1,24 @@
import {Component, inject, Input} from '@angular/core'; import {Component, EventEmitter, inject, OnDestroy, OnInit} from '@angular/core';
import { import {
MAT_DIALOG_DATA, MAT_DIALOG_DATA,
MatDialogActions, MatDialogActions,
MatDialogClose, MatDialogClose,
MatDialogContent, MatDialogContent, MatDialogRef,
MatDialogTitle MatDialogTitle
} from "@angular/material/dialog"; } from "@angular/material/dialog";
import {MatButton} from "@angular/material/button"; import {MatButton} from "@angular/material/button";
import {AsyncPipe} from "@angular/common";
import {MatProgressSpinner} from "@angular/material/progress-spinner";
import {Observable, Subscription} from "rxjs";
export interface IConfirmDialogDefaultOptions { export interface IConfirmDialogDefaultOptions {
title: string; title: string;
message: string; message: string;
confirmText?: string; confirmText?: string;
cancelText?: string; cancelText?: string;
success: Observable<any>;
failure: Observable<any>;
loading: Observable<boolean>;
} }
@Component({ @Component({
@ -23,16 +29,52 @@ export interface IConfirmDialogDefaultOptions {
MatDialogActions, MatDialogActions,
MatButton, MatButton,
MatDialogClose, MatDialogClose,
MatDialogTitle MatDialogTitle,
AsyncPipe,
MatProgressSpinner
], ],
templateUrl: './confirm-dialog-default.component.html', templateUrl: './confirm-dialog-default.component.html',
styleUrl: './confirm-dialog-default.component.css' styleUrl: './confirm-dialog-default.component.css'
}) })
export class ConfirmDialogDefaultComponent { export class ConfirmDialogDefaultComponent implements OnInit, OnDestroy {
readonly data = inject<IConfirmDialogDefaultOptions>(MAT_DIALOG_DATA) readonly data = inject<IConfirmDialogDefaultOptions>(MAT_DIALOG_DATA);
readonly ref = inject(MatDialogRef<ConfirmDialogDefaultComponent>);
readonly title: string = this.data.title; readonly title: string = this.data.title;
readonly message: string = this.data.message; readonly message: string = this.data.message;
readonly confirmText?: string = this.data.confirmText; readonly confirmText?: string = this.data.confirmText;
readonly cancelText?: string = this.data.cancelText; readonly cancelText?: string = this.data.cancelText;
readonly $loading: Observable<boolean> = this.data.loading;
readonly success: Observable<any> = this.data.success;
isLoading = false;
// todo: error messaging?
private subscriptions: Subscription[] = [];
private _onConfirm = new EventEmitter<void>;
private _disableCloseOriginalValue?: boolean;
get onConfirm() {
return this._onConfirm.asObservable();
}
ngOnInit(): void {
this._disableCloseOriginalValue = this.ref.disableClose;
this.subscriptions.push(this.$loading.subscribe(isLoading => {
this.isLoading = isLoading;
this.ref.disableClose = isLoading ? true : this._disableCloseOriginalValue;
}));
this.subscriptions.push(this.success.subscribe(_ => {
this.ref.close();
}));
}
ngOnDestroy() {
for (let subscription of this.subscriptions)
subscription.unsubscribe();
}
onConfirmed() {
this._onConfirm.emit();
}
} }

View File

@ -11,10 +11,5 @@
}, },
"exclude": [ "exclude": [
"**/*.spec.ts" "**/*.spec.ts"
], ]
/*"paths": {
"@openharbor/ngx-data-ui-core": [
"../core/src/public-api",
]
}*/
} }

View File

@ -12,7 +12,7 @@
"skipLibCheck": true, "skipLibCheck": true,
"paths": { "paths": {
"@openharbor/ngx-data-ui-core": [ "@openharbor/ngx-data-ui-core": [
"./projects/core/src/public-api" "./dist/openharbor/core"
], ],
"@openharbor/ngx-data-ui-md": [ "@openharbor/ngx-data-ui-md": [
"./projects/md-ui/src/public-api" "./projects/md-ui/src/public-api"

View File

@ -1996,10 +1996,17 @@
proc-log "^4.0.0" proc-log "^4.0.0"
which "^4.0.0" which "^4.0.0"
"@openharbor/data@^1.0.0-alpha.1": "@openharbor/data@1.0.0-alpha.4":
version "1.0.0-alpha.1" version "1.0.0-alpha.4"
resolved "https://registry.yarnpkg.com/@openharbor/data/-/data-1.0.0-alpha.1.tgz#69c2bf7a0e1bac6e54459d0123fd006dce86553a" resolved "https://registry.yarnpkg.com/@openharbor/data/-/data-1.0.0-alpha.4.tgz#6ae6161c6fe3b504f32d7c65ab0899164560abaa"
integrity sha512-MLcfYd8ZFLxcFi13z8PnRiKLnt2Hn1FbHiXts9KBix/R8FIOSeIRZwrOFIBSrYRqk+muBvif9IGYx+uxHQlk8Q== integrity sha512-WdlCmHETvwId3HmsGAeCNEcsJ4Rtydq5x/poM62wdAgBZJgu/I2XQ33Q5rFbctZeii2Cx5KHxsq+n9BrgA2/8A==
dependencies:
tslib "^2.3.0"
"@openharbor/ngx-data-ui-core@^18.0.0-alpha.1":
version "18.0.0-alpha.1"
resolved "https://registry.yarnpkg.com/@openharbor/ngx-data-ui-core/-/ngx-data-ui-core-18.0.0-alpha.1.tgz#a75ca3e98d4a4081736091706efa94c358de3e58"
integrity sha512-b8ullfXgOqWKHFh8+XZxihg7cT6NMZx7it1Zfpa1LHeU1VJh6lXWhi+lXzOmZPxm856ZJ+yWRTOQaxwmIqRoUw==
dependencies: dependencies:
tslib "^2.3.0" tslib "^2.3.0"