(fuse/confirmation) First iteration of the FuseConfirmationService

This commit is contained in:
sercan 2021-07-10 22:03:58 +03:00
parent d206c55e6e
commit 178d09597b
14 changed files with 766 additions and 3 deletions

View File

@ -1,5 +1,6 @@
import { NgModule, Optional, SkipSelf } from '@angular/core';
import { MAT_FORM_FIELD_DEFAULT_OPTIONS } from '@angular/material/form-field';
import { FuseConfirmationModule } from '@fuse/services/confirmation';
import { FuseMediaWatcherModule } from '@fuse/services/media-watcher/media-watcher.module';
import { FuseSplashScreenModule } from '@fuse/services/splash-screen/splash-screen.module';
import { FuseTailwindConfigModule } from '@fuse/services/tailwind/tailwind.module';
@ -7,6 +8,7 @@ import { FuseUtilsModule } from '@fuse/services/utils/utils.module';
@NgModule({
imports : [
FuseConfirmationModule,
FuseMediaWatcherModule,
FuseSplashScreenModule,
FuseTailwindConfigModule,

View File

@ -0,0 +1,31 @@
import { NgModule } from '@angular/core';
import { MatButtonModule } from '@angular/material/button';
import { MatDialogModule } from '@angular/material/dialog';
import { MatIconModule } from '@angular/material/icon';
import { FuseConfirmationService } from '@fuse/services/confirmation/confirmation.service';
import { FuseConfirmationDialogComponent } from '@fuse/services/confirmation/dialog/dialog.component';
import { CommonModule } from '@angular/common';
@NgModule({
declarations: [
FuseConfirmationDialogComponent
],
imports: [
MatButtonModule,
MatDialogModule,
MatIconModule,
CommonModule
],
providers : [
FuseConfirmationService
]
})
export class FuseConfirmationModule
{
/**
* Constructor
*/
constructor(private _fuseConfirmationService: FuseConfirmationService)
{
}
}

View File

@ -0,0 +1,57 @@
import { Injectable } from '@angular/core';
import { MatDialog, MatDialogRef } from '@angular/material/dialog';
import { merge } from 'lodash-es';
import { FuseConfirmationDialogComponent } from '@fuse/services/confirmation/dialog/dialog.component';
import { FuseConfirmationConfig } from '@fuse/services/confirmation/confirmation.types';
@Injectable()
export class FuseConfirmationService
{
private _defaultConfig: FuseConfirmationConfig = {
title : 'Confirm action',
message : 'Are you sure you want to confirm this action?',
icon : {
show : true,
name : 'heroicons_outline:exclamation',
color: 'warn'
},
actions : {
confirm: {
show : true,
label: 'Confirm',
color: 'warn'
},
cancel : {
show : true,
label: 'Cancel'
}
},
dismissible: false
};
/**
* Constructor
*/
constructor(
private _matDialog: MatDialog
)
{
}
// -----------------------------------------------------------------------------------------------------
// @ Public methods
// -----------------------------------------------------------------------------------------------------
open(config: FuseConfirmationConfig = {}): MatDialogRef<FuseConfirmationDialogComponent>
{
// Merge the user config with the default config
const userConfig = merge({}, this._defaultConfig, config);
// Open the dialog
return this._matDialog.open(FuseConfirmationDialogComponent, {
autoFocus : false,
disableClose: !userConfig.dismissible,
data : userConfig
});
}
}

View File

@ -0,0 +1,22 @@
export interface FuseConfirmationConfig
{
title?: string;
message?: string;
icon?: {
show?: boolean;
name?: string;
color?: 'primary' | 'accent' | 'warn' | 'basic' | 'info' | 'success' | 'warning' | 'error';
};
actions?: {
confirm?: {
show?: boolean;
label?: string;
color?: 'primary' | 'accent' | 'warn';
};
cancel?: {
show?: boolean;
label?: string;
};
};
dismissible?: boolean;
}

View File

@ -0,0 +1,85 @@
<div class="relative flex flex-col md:w-128 -m-6">
<!-- Dismiss button -->
<ng-container *ngIf="data.dismissible">
<div class="absolute top-0 right-0 pt-4 pr-4">
<button
mat-icon-button
[matDialogClose]="undefined">
<mat-icon
class="text-secondary"
[svgIcon]="'heroicons_outline:x'"></mat-icon>
</button>
</div>
</ng-container>
<!-- Content -->
<div class="flex flex-col sm:flex-row items-center sm:items-start p-8 pb-6 sm:pb-8">
<!-- Icon -->
<ng-container *ngIf="data.icon.show">
<div
class="flex flex-0 items-center justify-center w-10 h-10 sm:mr-4 rounded-full"
[ngClass]="{'text-primary-600 bg-primary-100': data.icon.color === 'primary',
'text-accent-600 bg-accent-100': data.icon.color === 'accent',
'text-warn-600 bg-warn-100': data.icon.color === 'warn',
'text-gray-600 bg-gray-100': data.icon.color === 'basic',
'text-blue-600 bg-blue-100': data.icon.color === 'info',
'text-green-500 bg-green-100': data.icon.color === 'success',
'text-amber-500 bg-amber-100': data.icon.color === 'warning',
'text-red-600 bg-red-100': data.icon.color === 'error'
}">
<mat-icon
class="text-current"
[svgIcon]="data.icon.name"></mat-icon>
</div>
</ng-container>
<ng-container *ngIf="data.title || data.message">
<div class="flex flex-col items-center sm:items-start mt-4 sm:mt-0 sm:pr-8 space-y-1 text-center sm:text-left">
<!-- Title -->
<ng-container *ngIf="data.title">
<div
class="text-xl leading-6 font-medium"
[innerHTML]="data.title"></div>
</ng-container>
<!-- Message -->
<ng-container *ngIf="data.message">
<div
class="text-secondary"
[innerHTML]="data.message"></div>
</ng-container>
</div>
</ng-container>
</div>
<!-- Buttons -->
<ng-container *ngIf="data.actions.confirm.show || data.actions.cancel.show">
<div class="flex items-center justify-center sm:justify-end px-6 py-4 space-x-3 bg-gray-50">
<!-- Cancel -->
<ng-container *ngIf="data.actions.cancel.show">
<button
mat-stroked-button
[matDialogClose]="'cancelled'">
{{data.actions.cancel.label}}
</button>
</ng-container>
<!-- Confirm -->
<ng-container *ngIf="data.actions.confirm.show">
<button
mat-flat-button
[color]="data.actions.confirm.color"
[matDialogClose]="'confirmed'">
{{data.actions.confirm.label}}
</button>
</ng-container>
</div>
</ng-container>
</div>

View File

@ -0,0 +1,38 @@
import { Component, Inject, OnInit, ViewEncapsulation } from '@angular/core';
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
import { FuseConfirmationConfig } from '@fuse/services/confirmation/confirmation.types';
@Component({
selector : 'fuse-confirmation-dialog',
templateUrl : './dialog.component.html',
encapsulation: ViewEncapsulation.None
})
export class FuseConfirmationDialogComponent implements OnInit
{
/**
* Constructor
*/
constructor(
@Inject(MAT_DIALOG_DATA) public data: FuseConfirmationConfig,
public matDialogRef: MatDialogRef<FuseConfirmationDialogComponent>
)
{
}
// -----------------------------------------------------------------------------------------------------
// @ Lifecycle hooks
// -----------------------------------------------------------------------------------------------------
/**
* On init
*/
ngOnInit(): void
{
}
// -----------------------------------------------------------------------------------------------------
// @ Public methods
// -----------------------------------------------------------------------------------------------------
}

View File

@ -0,0 +1 @@
export * from '@fuse/services/confirmation/public-api';

View File

@ -0,0 +1,3 @@
export * from '@fuse/services/confirmation/confirmation.module';
export * from '@fuse/services/confirmation/confirmation.service';
export * from '@fuse/services/confirmation/confirmation.types';

View File

@ -73,8 +73,7 @@
<h2>Navigation item</h2>
<p>
This is the type alias for the <em>Navigation item</em>. It's used to create the navigation and both <em>vertical</em> and <em>horizontal</em> variations use the
same item type:
This is the interface of the <em>Navigation item</em>. Both <em>vertical</em> and <em>horizontal</em> navigation items use the same interface:
</p>
<!-- @formatter:off -->
<textarea fuse-highlight

View File

@ -127,6 +127,12 @@ export class FuseComponentsComponent implements OnInit, OnDestroy
type : 'basic',
link : '/ui/fuse-components/services/config'
},
{
id : 'fuse-components.services.confirmation',
title: 'Confirmation',
type : 'basic',
link : '/ui/fuse-components/services/confirmation'
},
{
id : 'fuse-components.services.splash-screen',
title: 'SplashScreen',

View File

@ -1,8 +1,12 @@
import { NgModule } from '@angular/core';
import { RouterModule } from '@angular/router';
import { MatButtonModule } from '@angular/material/button';
import { MatFormFieldModule } from '@angular/material/form-field';
import { MatIconModule } from '@angular/material/icon';
import { MatInputModule } from '@angular/material/input';
import { MatSelectModule } from '@angular/material/select';
import { MatSidenavModule } from '@angular/material/sidenav';
import { MatSlideToggleModule } from '@angular/material/slide-toggle';
import { MatTabsModule } from '@angular/material/tabs';
import { MatTreeModule } from '@angular/material/tree';
import { FuseCardModule } from '@fuse/components/card';
@ -27,6 +31,7 @@ import { MasonryComponent } from 'app/modules/admin/ui/fuse-components/component
import { ScrollbarComponent } from 'app/modules/admin/ui/fuse-components/directives/scrollbar/scrollbar.component';
import { ScrollResetComponent } from 'app/modules/admin/ui/fuse-components/directives/scroll-reset/scroll-reset.component';
import { ConfigComponent } from 'app/modules/admin/ui/fuse-components/services/config/config.component';
import { ConfirmationComponent } from 'app/modules/admin/ui/fuse-components/services/confirmation/confirmation.component';
import { MediaWatcherComponent } from 'app/modules/admin/ui/fuse-components/services/media-watcher/media-watcher.component';
import { SplashScreenComponent } from 'app/modules/admin/ui/fuse-components/services/splash-screen/splash-screen.component';
import { FindByKeyComponent } from 'app/modules/admin/ui/fuse-components/pipes/find-by-key/find-by-key.component';
@ -48,16 +53,21 @@ import { fuseComponentsRoutes } from 'app/modules/admin/ui/fuse-components/fuse-
ScrollbarComponent,
ScrollResetComponent,
ConfigComponent,
ConfirmationComponent,
SplashScreenComponent,
MediaWatcherComponent,
FindByKeyComponent,
MustMatchComponent
],
imports : [
imports: [
RouterModule.forChild(fuseComponentsRoutes),
MatButtonModule,
MatFormFieldModule,
MatIconModule,
MatInputModule,
MatSelectModule,
MatSidenavModule,
MatSlideToggleModule,
MatTabsModule,
MatTreeModule,
FuseAlertModule,

View File

@ -12,6 +12,7 @@ import { NavigationComponent } from 'app/modules/admin/ui/fuse-components/compon
import { ScrollbarComponent } from 'app/modules/admin/ui/fuse-components/directives/scrollbar/scrollbar.component';
import { ScrollResetComponent } from 'app/modules/admin/ui/fuse-components/directives/scroll-reset/scroll-reset.component';
import { ConfigComponent } from 'app/modules/admin/ui/fuse-components/services/config/config.component';
import { ConfirmationComponent } from 'app/modules/admin/ui/fuse-components/services/confirmation/confirmation.component';
import { MediaWatcherComponent } from 'app/modules/admin/ui/fuse-components/services/media-watcher/media-watcher.component';
import { SplashScreenComponent } from 'app/modules/admin/ui/fuse-components/services/splash-screen/splash-screen.component';
import { FindByKeyComponent } from 'app/modules/admin/ui/fuse-components/pipes/find-by-key/find-by-key.component';
@ -108,6 +109,10 @@ export const fuseComponentsRoutes: Route[] = [
path : 'config',
component: ConfigComponent
},
{
path : 'confirmation',
component: ConfirmationComponent
},
{
path : 'splash-screen',
component: SplashScreenComponent

View File

@ -0,0 +1,420 @@
<div class="flex flex-col flex-auto min-w-0">
<!-- Header -->
<div class="flex flex-col sm:flex-row flex-0 sm:items-center sm:justify-between p-6 sm:py-8 sm:px-10 border-b bg-card dark:bg-transparent">
<div class="flex-1 min-w-0">
<!-- Breadcrumbs -->
<div class="flex flex-wrap items-center font-medium">
<div>
<a class="whitespace-nowrap text-primary-500">Documentation</a>
</div>
<div class="flex items-center ml-1 whitespace-nowrap">
<mat-icon
class="icon-size-5 text-secondary"
[svgIcon]="'heroicons_solid:chevron-right'"></mat-icon>
<a class="ml-1 text-primary-500">Fuse Components</a>
</div>
<div class="flex items-center ml-1 whitespace-nowrap">
<mat-icon
class="icon-size-5 text-secondary"
[svgIcon]="'heroicons_solid:chevron-right'"></mat-icon>
<span class="ml-1 text-secondary">Services</span>
</div>
</div>
<!-- Title -->
<div class="mt-2">
<h2 class="text-3xl md:text-4xl font-extrabold tracking-tight leading-7 sm:leading-10 truncate">
Confirmation
</h2>
</div>
</div>
<button
class="-ml-3 sm:ml-0 mb-2 sm:mb-0 order-first sm:order-last"
mat-icon-button
(click)="toggleDrawer()">
<mat-icon [svgIcon]="'heroicons_outline:menu'"></mat-icon>
</button>
</div>
<div class="flex-auto max-w-3xl p-6 sm:p-10 prose prose-sm">
<p>
<strong>FuseConfirmationService</strong> is a singleton service for creating confirmation and information dialogs. Internally it uses MatDialog to create and
show the dialog.
</p>
<h2>Module</h2>
<textarea
fuse-highlight
lang="typescript">
import { FuseConfirmationModule } from '@fuse/services/confirmation';
</textarea>
<h2>Confirmation Config</h2>
<p>
Here is the interface for the <em>Confirmation configuration</em>:
</p>
<!-- @formatter:off -->
<textarea fuse-highlight
lang="typescript">
export interface FuseConfirmationConfig
{
title?: string;
message?: string;
icon?: {
show?: boolean;
name?: string;
color?:
| 'primary'
| 'accent'
| 'warn'
| 'basic'
| 'info'
| 'success'
| 'warning'
| 'error';
};
actions: {
confirm?: {
show?: boolean;
label?: string;
color?:
| 'primary'
| 'accent'
| 'warn';
};
cancel?: {
show?: boolean;
label?: string;
};
};
dismissible?: boolean;
}
</textarea>
<!-- @formatter:on -->
<div class="bg-card py-3 px-6 mt-2 rounded shadow">
<table>
<thead>
<tr>
<th>Name</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td class="font-mono text-md text-secondary">
<div>title</div>
</td>
<td>
Title of the confirmation dialog.
</td>
</tr>
<tr>
<td class="font-mono text-md text-secondary">
<div>message</div>
</td>
<td>
Message of the confirmation dialog.
</td>
</tr>
<tr>
<td class="font-mono text-md text-secondary">
<div>icon.show</div>
</td>
<td>
Whether to show the icon.
</td>
</tr>
<tr>
<td class="font-mono text-md text-secondary">
<div>icon.name</div>
</td>
<td>
Name of the icon.
</td>
</tr>
<tr>
<td class="font-mono text-md text-secondary">
<div>icon.color</div>
</td>
<td>
Color of the icon.
</td>
</tr>
<tr>
<td class="font-mono text-md text-secondary">
<div>actions.confirm.show</div>
</td>
<td>
Whether to show the confirmation button.
</td>
</tr>
<tr>
<td class="font-mono text-md text-secondary">
<div>actions.confirm.label</div>
</td>
<td>
Label of the confirmation button.
</td>
</tr>
<tr>
<td class="font-mono text-md text-secondary">
<div>actions.confirm.color</div>
</td>
<td>
Color of the confirmation button.
</td>
</tr>
<tr>
<td class="font-mono text-md text-secondary">
<div>actions.cancel.show</div>
</td>
<td>
Whether to show the cancel button.
</td>
</tr>
<tr>
<td class="font-mono text-md text-secondary">
<div>actions.confirm.label</div>
</td>
<td>
Label of the cancel button.
</td>
</tr>
<tr>
<td class="font-mono text-md text-secondary">
<div>dismissible</div>
</td>
<td>
Sets the dismissible status of the confirmation dialog.<br>
If <code>false</code>, confirmation dialog cannot be closed by clicking on backdrop or pressing Escape key.
The close button on the top right corner also won't show up.
</td>
</tr>
</tbody>
</table>
</div>
<h2>Methods</h2>
<div class="bg-card rounded shadow mt-4">
<div class="px-6 py-3 font-mono text-secondary border-b">
open(config: FuseConfirmationConfig): MatDialogRef&lt;FuseConfirmationDialogComponent&gt;
</div>
<div class="p-6">
Opens the confirmation dialog with the given configuration
</div>
</div>
<h2>MatDialogRef</h2>
<p>
Since <code>FuseConfirmationService</code> uses <em>MatDialog</em> behind the scenes, it returns
a reference to the created dialog. Using that reference, you can access to the user input:
</p>
<!-- @formatter:off -->
<textarea
fuse-highlight
[lang]="'ts'">
// Open the confirmation and save the reference
const dialogRef = this._fuseConfirmationService.open({...});
// Subscribe to afterClosed from the dialog reference
dialogRef.afterClosed().subscribe((result) => {
console.log(result);
});
</textarea>
<!-- @formatter:on -->
<div class="bg-card py-3 px-6 mt-2 rounded shadow">
<table>
<thead>
<tr>
<th>Result</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td class="font-mono text-md text-secondary">
<div><code>'confirmed'</code></div>
</td>
<td>
This is the result if the user pressed the Confirm button.
</td>
</tr>
<tr>
<td class="font-mono text-md text-secondary">
<div><code>'cancelled'</code></div>
</td>
<td>
This is the result if the user pressed the Cancel button.
</td>
</tr>
<tr>
<td class="font-mono text-md text-secondary">
<div><code>undefined</code></div>
</td>
<td>
This is the result if the confirmation dismissed either using the close button,
clicking on the backdrop or pressing the Escape key.
</td>
</tr>
</tbody>
</table>
</div>
<h2>Example</h2>
<p>
Below you can configure the dialog and preview it. You can use the generated configuration object within your code
to have the exact same dialog.
</p>
<div class="example-viewer">
<div class="title">
<h6>Configure the dialog and preview it</h6>
</div>
<div class="flex flex-col p-8 pt-0">
<form
[formGroup]="configForm"
class="flex flex-col items-start">
<!-- Title -->
<mat-form-field class="fuse-mat-no-subscript w-full">
<mat-label>Title</mat-label>
<input
matInput
[formControlName]="'title'">
</mat-form-field>
<!-- Message -->
<mat-form-field class="fuse-mat-no-subscript fuse-mat-textarea w-full mt-6">
<mat-label>Message</mat-label>
<textarea
matInput
[formControlName]="'message'">
</textarea>
</mat-form-field>
<!-- Divider -->
<div class="w-full mt-8 mb-7 border-b"></div>
<!-- Icon -->
<div
class="flex flex-col w-full"
[formGroupName]="'icon'">
<mat-slide-toggle
[color]="'primary'"
[formControlName]="'show'">
Show Icon
</mat-slide-toggle>
<div class="flex items-center w-full mt-6">
<!-- Icon name -->
<mat-form-field class="fuse-mat-no-subscript w-1/2 pr-2">
<mat-label>Icon name</mat-label>
<input
matInput
[formControlName]="'name'">
</mat-form-field>
<!-- Icon color -->
<mat-form-field class="fuse-mat-no-subscript w-1/2 pl-2">
<mat-label>Icon color</mat-label>
<mat-select [formControlName]="'color'">
<ng-container *ngFor="let color of ['primary', 'accent', 'warn', 'basic', 'info', 'success', 'warning', 'error']">
<mat-option [value]="color">{{color | titlecase}}</mat-option>
</ng-container>
</mat-select>
</mat-form-field>
</div>
</div>
<!-- Divider -->
<div class="w-full mt-8 mb-7 border-b"></div>
<!-- Actions -->
<div
class="w-full"
[formGroupName]="'actions'">
<!-- Confirm -->
<div
class="flex flex-col w-full"
[formGroupName]="'confirm'">
<mat-slide-toggle
class="mt-2"
[color]="'primary'"
[formControlName]="'show'">
Show Confirm button
</mat-slide-toggle>
<div class="flex items-center w-full mt-4">
<!-- Confirm label -->
<mat-form-field class="fuse-mat-no-subscript w-1/2 pr-2">
<mat-label>Confirm button label</mat-label>
<input
matInput
[formControlName]="'label'">
</mat-form-field>
<!-- Confirm color -->
<mat-form-field class="fuse-mat-no-subscript w-1/2 pl-2">
<mat-label>Confirm button color</mat-label>
<mat-select [formControlName]="'color'">
<ng-container *ngFor="let color of ['primary', 'accent', 'warn']">
<mat-option [value]="color">{{color | titlecase}}</mat-option>
</ng-container>
</mat-select>
</mat-form-field>
</div>
</div>
<!-- Cancel -->
<div
class="flex flex-col w-full mt-6"
[formGroupName]="'cancel'">
<mat-slide-toggle
class="mt-2"
[color]="'primary'"
[formControlName]="'show'">
Show Cancel button
</mat-slide-toggle>
<div class="flex items-center w-full mt-4">
<!-- Cancel label -->
<mat-form-field class="fuse-mat-no-subscript w-1/2 pr-2">
<mat-label>Cancel button label</mat-label>
<input
matInput
[formControlName]="'label'">
</mat-form-field>
</div>
</div>
</div>
<!-- Divider -->
<div class="w-full mt-8 mb-7 border-b"></div>
<!-- Dismissible -->
<mat-slide-toggle
[color]="'primary'"
[formControlName]="'dismissible'">
Dismissible
</mat-slide-toggle>
</form>
<div class="mt-12">
<button
mat-flat-button
[color]="'primary'"
(click)="openConfirmationDialog()">
Open Confirmation Dialog
</button>
</div>
</div>
</div>
<!-- Config as json -->
<textarea
fuse-highlight
[code]="configForm.value | json"
[lang]="'json'"></textarea>
</div>
</div>

View File

@ -0,0 +1,84 @@
import { Component, OnInit } from '@angular/core';
import { FormBuilder, FormGroup } from '@angular/forms';
import { FuseConfirmationService } from '@fuse/services/confirmation';
import { FuseComponentsComponent } from 'app/modules/admin/ui/fuse-components/fuse-components.component';
@Component({
selector : 'confirmation',
templateUrl: './confirmation.component.html'
})
export class ConfirmationComponent implements OnInit
{
configForm: FormGroup;
/**
* Constructor
*/
constructor(
private _formBuilder: FormBuilder,
private _fuseConfirmationService: FuseConfirmationService,
private _fuseComponentsComponent: FuseComponentsComponent
)
{
}
// -----------------------------------------------------------------------------------------------------
// @ Lifecycle hooks
// -----------------------------------------------------------------------------------------------------
/**
* On init
*/
ngOnInit(): void
{
// Build the config form
this.configForm = this._formBuilder.group({
title : 'Remove contact',
message : 'Are you sure you want to remove this contact permanently? <span class="font-medium">This action cannot be undone!</span>',
icon : this._formBuilder.group({
show : true,
name : 'heroicons_outline:exclamation',
color: 'warn'
}),
actions : this._formBuilder.group({
confirm: this._formBuilder.group({
show : true,
label: 'Remove',
color: 'warn'
}),
cancel : this._formBuilder.group({
show : true,
label: 'Cancel'
})
}),
dismissible: true
});
}
// -----------------------------------------------------------------------------------------------------
// @ Public methods
// -----------------------------------------------------------------------------------------------------
/**
* Open confirmation dialog
*/
openConfirmationDialog(): void
{
// Open the dialog and save the reference of it
const dialogRef = this._fuseConfirmationService.open(this.configForm.value);
// Subscribe to afterClosed from the dialog reference
dialogRef.afterClosed().subscribe((result) => {
console.log(result);
});
}
/**
* Toggle the drawer
*/
toggleDrawer(): void
{
// Toggle the drawer
this._fuseComponentsComponent.matDrawer.toggle();
}
}